diff --git a/Gemfile b/Gemfile index ce8a6470..1f3e1a4f 100644 --- a/Gemfile +++ b/Gemfile @@ -6,7 +6,7 @@ gem "activesupport", require: false gem "mry", require: false gem "parser" gem "pry", require: false -gem "rubocop", "1.22.3", require: false +gem "rubocop", "1.23.0", require: false gem "rubocop-i18n", require: false gem "rubocop-graphql", require: false gem "rubocop-minitest", require: false diff --git a/Gemfile.lock b/Gemfile.lock index 9b7c8fd2..3ffb3b09 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -18,7 +18,7 @@ GEM mry (0.78.0.0) rubocop (>= 0.41.0) parallel (1.21.0) - parser (3.0.2.0) + parser (3.0.3.1) ast (~> 2.4.1) pry (0.14.1) coderay (~> 1.1) @@ -26,7 +26,7 @@ GEM rack (2.2.3) rainbow (3.0.0) rake (13.0.6) - regexp_parser (2.1.1) + regexp_parser (2.2.0) rexml (3.2.5) rspec (3.10.0) rspec-core (~> 3.10.0) @@ -41,7 +41,7 @@ GEM diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.10.0) rspec-support (3.10.3) - rubocop (1.22.3) + rubocop (1.23.0) parallel (~> 1.10) parser (>= 3.0.0.0) rainbow (>= 2.2.2, < 4.0) @@ -50,13 +50,13 @@ GEM rubocop-ast (>= 1.12.0, < 2.0) ruby-progressbar (~> 1.7) unicode-display_width (>= 1.4.0, < 3.0) - rubocop-ast (1.12.0) + rubocop-ast (1.14.0) parser (>= 3.0.1.1) - rubocop-graphql (0.10.2) + rubocop-graphql (0.11.2) rubocop (>= 0.87, < 2) rubocop-i18n (3.0.0) rubocop (~> 1.0) - rubocop-minitest (0.15.2) + rubocop-minitest (0.17.0) rubocop (>= 0.90, < 2.0) rubocop-performance (1.12.0) rubocop (>= 1.7.0, < 2.0) @@ -67,12 +67,12 @@ GEM rubocop (>= 1.7.0, < 2.0) rubocop-rake (0.6.0) rubocop (~> 1.0) - rubocop-rspec (2.5.0) + rubocop-rspec (2.6.0) rubocop (~> 1.19) rubocop-sequel (0.3.3) rubocop (~> 1.0) - rubocop-sorbet (0.6.2) - rubocop + rubocop-sorbet (0.6.3) + rubocop (>= 0.90.0) rubocop-thread_safety (0.4.4) rubocop (>= 0.53.0) ruby-progressbar (1.11.0) @@ -93,7 +93,7 @@ DEPENDENCIES pry rake rspec - rubocop (= 1.22.3) + rubocop (= 1.23.0) rubocop-graphql rubocop-i18n rubocop-minitest diff --git a/config/contents/gemspec/require_mfa.md b/config/contents/gemspec/require_mfa.md new file mode 100644 index 00000000..594c32ec --- /dev/null +++ b/config/contents/gemspec/require_mfa.md @@ -0,0 +1,55 @@ +Requires a gemspec to have `rubygems_mfa_required` metadata set. + +This setting tells RubyGems that MFA is required for accounts to +be able perform any of these privileged operations: + +* gem push +* gem yank +* gem owner --add/remove +* adding or removing owners using gem ownership page + +This helps make your gem more secure, as users can be more +confident that gem updates were pushed by maintainers. + +### Example: + + # bad + Gem::Specification.new do |spec| + # no `rubygems_mfa_required` metadata specified + end + + # good + Gem::Specification.new do |spec| + spec.metadata = { + 'rubygems_mfa_required' => 'true' + } + end + + # good + Gem::Specification.new do |spec| + spec.metadata['rubygems_mfa_required'] = 'true' + end + + # bad + Gem::Specification.new do |spec| + spec.metadata = { + 'rubygems_mfa_required' => 'false' + } + end + + # good + Gem::Specification.new do |spec| + spec.metadata = { + 'rubygems_mfa_required' => 'true' + } + end + + # bad + Gem::Specification.new do |spec| + spec.metadata['rubygems_mfa_required'] = 'false' + end + + # good + Gem::Specification.new do |spec| + spec.metadata['rubygems_mfa_required'] = 'true' + end diff --git a/config/contents/layout/empty_line_between_defs.md b/config/contents/layout/empty_line_between_defs.md index 100f67b8..a305a56a 100644 --- a/config/contents/layout/empty_line_between_defs.md +++ b/config/contents/layout/empty_line_between_defs.md @@ -72,9 +72,30 @@ one-line definitions are considered an offense. def b end -### Example: AllowAdjacentOneLineDefs: true +### Example: AllowAdjacentOneLineDefs: true (default) # good class ErrorA < BaseError; end class ErrorB < BaseError; end class ErrorC < BaseError; end + + # good + class ErrorA < BaseError; end + + class ErrorB < BaseError; end + + class ErrorC < BaseError; end + +### Example: AllowAdjacentOneLineDefs: false + + # bad + class ErrorA < BaseError; end + class ErrorB < BaseError; end + class ErrorC < BaseError; end + + # good + class ErrorA < BaseError; end + + class ErrorB < BaseError; end + + class ErrorC < BaseError; end diff --git a/config/contents/lint/ambiguous_range.md b/config/contents/lint/ambiguous_range.md index 9c8f5564..2ffcbb06 100644 --- a/config/contents/lint/ambiguous_range.md +++ b/config/contents/lint/ambiguous_range.md @@ -3,7 +3,7 @@ This cop checks for ambiguous ranges. Ranges have quite low precedence, which leads to unexpected behaviour when using a range with other operators. This cop avoids that by making ranges explicit by requiring parenthesis around complex range boundaries (anything -that is not a basic literal: numerics, strings, symbols, etc.). +that is not a literal: numerics, strings, symbols, etc.). This cop can be configured with `RequireParenthesesForMethodChains` in order to specify whether method chains (including `self.foo`) should be wrapped in parens diff --git a/config/contents/lint/number_conversion.md b/config/contents/lint/number_conversion.md index ccd97fbd..ef417e99 100644 --- a/config/contents/lint/number_conversion.md +++ b/config/contents/lint/number_conversion.md @@ -26,6 +26,7 @@ input if it is not a standard class. '10'.to_i '10.2'.to_f '10'.to_c + '1/3'.to_r ['1', '2', '3'].map(&:to_i) foo.try(:to_f) bar.send(:to_c) @@ -35,6 +36,7 @@ input if it is not a standard class. Integer('10', 10) Float('10.2') Complex('10') + Rational('1/3') ['1', '2', '3'].map { |i| Integer(i, 10) } foo.try { |i| Float(i) } bar.send { |i| Complex(i) } diff --git a/config/contents/lint/useless_ruby2_keywords.md b/config/contents/lint/useless_ruby2_keywords.md new file mode 100644 index 00000000..d492d4fd --- /dev/null +++ b/config/contents/lint/useless_ruby2_keywords.md @@ -0,0 +1,59 @@ +This cop looks for `ruby2_keywords` calls for methods that do not need it. + +`ruby2_keywords` should only be called on methods that accept an argument splat +(`*args`) but do not explicit keyword arguments (`k:` or `k: true`) or +a keyword splat (`**kwargs`). + +### Example: + # good (splat argument without keyword arguments) + ruby2_keywords def foo(*args); end + + # bad (no arguments) + ruby2_keywords def foo; end + + # good + def foo; end + + # bad (positional argument) + ruby2_keywords def foo(arg); end + + # good + def foo(arg); end + + # bad (double splatted argument) + ruby2_keywords def foo(**args); end + + # good + def foo(**args); end + + # bad (keyword arguments) + ruby2_keywords def foo(i:, j:); end + + # good + def foo(i:, j:); end + + # bad (splat argument with keyword arguments) + ruby2_keywords def foo(*args, i:, j:); end + + # good + def foo(*args, i:, j:); end + + # bad (splat argument with double splat) + ruby2_keywords def foo(*args, **kwargs); end + + # good + def foo(*args, **kwargs); end + + # bad (ruby2_keywords given a symbol) + def foo; end + ruby2_keywords :foo + + # good + def foo; end + + # bad (ruby2_keywords with dynamic method) + define_method(:foo) { |arg| } + ruby2_keywords :foo + + # good + define_method(:foo) { |arg| } diff --git a/config/contents/metrics/parameter_lists.md b/config/contents/metrics/parameter_lists.md index fb232dd3..b8d5ea83 100644 --- a/config/contents/metrics/parameter_lists.md +++ b/config/contents/metrics/parameter_lists.md @@ -5,7 +5,7 @@ Keyword arguments can optionally be excluded from the total count, as they add less complexity than positional or optional parameters. NOTE: Explicit block argument `&block` is not counted to prevent - erroneous change that is avoided by making block argument implicit. +erroneous change that is avoided by making block argument implicit. ### Example: Max: 3 # good diff --git a/config/contents/naming/file_name.md b/config/contents/naming/file_name.md index b6b9e260..325e5137 100644 --- a/config/contents/naming/file_name.md +++ b/config/contents/naming/file_name.md @@ -7,6 +7,18 @@ recommends using dashes to separate namespaces in nested gems (i.e. `bundler-console` becomes `Bundler::Console`). As such, the gemspec is supposed to be named `bundler-console.gemspec`. +When `ExpectMatchingDefinition` (default: `false`) is `true`, the cop requires +each file to have a class, module or `Struct` defined in it that matches +the filename. This can be further configured using +`CheckDefinitionPathHierarchy` (default: `true`) to determine whether the +path should match the namespace of the above definition. + +When `IgnoreExecutableScripts` (default: `true`) is `true`, files that start +with a shebang line are not considered by the cop. + +When `Regex` is set, the cop will flag any filename that does not match +the regular expression. + ### Example: # bad lib/layoutManager.rb diff --git a/config/contents/style/empty_method.md b/config/contents/style/empty_method.md index 48673baa..32108609 100644 --- a/config/contents/style/empty_method.md +++ b/config/contents/style/empty_method.md @@ -4,7 +4,7 @@ line (compact style), but it can be configured to enforce the `end` to go on its own line (expanded style). NOTE: A method definition is not considered empty if it contains - comments. +comments. ### Example: EnforcedStyle: compact (default) # bad diff --git a/config/contents/style/open_struct_use.md b/config/contents/style/open_struct_use.md new file mode 100644 index 00000000..c9bb040b --- /dev/null +++ b/config/contents/style/open_struct_use.md @@ -0,0 +1,38 @@ +This cop flags uses of OpenStruct, as it is now officially discouraged +to be used for performance, version compatibility, and potential security issues. + +### Safety: + + +Note that this cop may flag false positives; for instance, the following legal +use of a hand-rolled `OpenStruct` type would be considered an offense: + +``` +module MyNamespace + class OpenStruct # not the OpenStruct we're looking for + end + + def new_struct + OpenStruct.new # resolves to MyNamespace::OpenStruct + end +end +``` + +### Example: + + # bad + point = OpenStruct.new(x: 0, y: 1) + + # good + Point = Struct.new(:x, :y) + point = Point.new(0, 1) + + # also good + point = { x: 0, y: 1 } + + # bad + test_double = OpenStruct.new(a: 'b') + + # good (assumes test using rspec-mocks) + test_double = double + allow(test_double).to receive(:a).and_return('b') diff --git a/spec/support/config_upgrader_rubocop.yml b/spec/support/config_upgrader_rubocop.yml index b6bf9696..f64f23e5 100644 --- a/spec/support/config_upgrader_rubocop.yml +++ b/spec/support/config_upgrader_rubocop.yml @@ -3,6 +3,14 @@ Style/AccessorMethodName: Style/FrozenStringLiteralComment: Enabled: false +# 1.23 +Gemspec/RequireMFA: + Enabled: true +Lint/UselessRuby2Keywords: + Enabled: true +Style/OpenStructUse: + Enabled: true + # 1.22 Lint/RequireRelativeSelfPath: Enabled: true