diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index d6a65b650..3846efa04 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -14,4 +14,4 @@ InternalAffairs/MethodNameEqual: # Offense count: 1 # Configuration parameters: CountComments. Metrics/ClassLength: - Max: 104 + Max: 106 diff --git a/lib/rubocop/cop/rspec/align_left_let_brace.rb b/lib/rubocop/cop/rspec/align_left_let_brace.rb index 08669f3aa..b2f307642 100644 --- a/lib/rubocop/cop/rspec/align_left_let_brace.rb +++ b/lib/rubocop/cop/rspec/align_left_let_brace.rb @@ -18,35 +18,28 @@ module RSpec # let(:a) { b } # class AlignLeftLetBrace < Cop + extend AutoCorrector + MSG = 'Align left let brace' def self.autocorrect_incompatible_with [Layout::ExtraSpacing] end - def investigate(_processed_source) + def on_new_investigation return if processed_source.blank? - token_aligner.offending_tokens.each do |let| - add_offense(let, location: :begin) - end - end + token_aligner = + RuboCop::RSpec::AlignLetBrace.new(processed_source.ast, :begin) - def autocorrect(let) - lambda do |corrector| - corrector.insert_before( - let.loc.begin, - token_aligner.indent_for(let) - ) + token_aligner.offending_tokens.each do |let| + add_offense(let.loc.begin) do |corrector| + corrector.insert_before( + let.loc.begin, token_aligner.indent_for(let) + ) + end end end - - private - - def token_aligner - @token_aligner ||= - RuboCop::RSpec::AlignLetBrace.new(processed_source.ast, :begin) - end end end end diff --git a/lib/rubocop/cop/rspec/align_right_let_brace.rb b/lib/rubocop/cop/rspec/align_right_let_brace.rb index 0526a09ac..387d9507b 100644 --- a/lib/rubocop/cop/rspec/align_right_let_brace.rb +++ b/lib/rubocop/cop/rspec/align_right_let_brace.rb @@ -18,35 +18,28 @@ module RSpec # let(:a) { b } # class AlignRightLetBrace < Cop + extend AutoCorrector + MSG = 'Align right let brace' def self.autocorrect_incompatible_with [Layout::ExtraSpacing] end - def investigate(_processed_source) + def on_new_investigation return if processed_source.blank? - token_aligner.offending_tokens.each do |let| - add_offense(let, location: :end) - end - end + token_aligner = + RuboCop::RSpec::AlignLetBrace.new(processed_source.ast, :end) - def autocorrect(let) - lambda do |corrector| - corrector.insert_before( - let.loc.end, - token_aligner.indent_for(let) - ) + token_aligner.offending_tokens.each do |let| + add_offense(let.loc.end) do |corrector| + corrector.insert_before( + let.loc.end, token_aligner.indent_for(let) + ) + end end end - - private - - def token_aligner - @token_aligner ||= - RuboCop::RSpec::AlignLetBrace.new(processed_source.ast, :end) - end end end end diff --git a/lib/rubocop/cop/rspec/be.rb b/lib/rubocop/cop/rspec/be.rb index 503648e12..a70cb0044 100644 --- a/lib/rubocop/cop/rspec/be.rb +++ b/lib/rubocop/cop/rspec/be.rb @@ -28,7 +28,7 @@ class Be < Cop def on_send(node) be_without_args(node) do |matcher| - add_offense(matcher, location: :selector) + add_offense(matcher.loc.selector) end end end diff --git a/lib/rubocop/cop/rspec/be_eql.rb b/lib/rubocop/cop/rspec/be_eql.rb index 0721c8c53..ad13a904a 100644 --- a/lib/rubocop/cop/rspec/be_eql.rb +++ b/lib/rubocop/cop/rspec/be_eql.rb @@ -36,6 +36,8 @@ module RSpec # coerce objects for comparison. # class BeEql < Cop + extend AutoCorrector + MSG = 'Prefer `be` over `eql`.' def_node_matcher :eql_type_with_identity, <<-PATTERN @@ -44,13 +46,11 @@ class BeEql < Cop def on_send(node) eql_type_with_identity(node) do |eql| - add_offense(eql, location: :selector) + add_offense(eql.loc.selector) do |corrector| + corrector.replace(eql.loc.selector, 'be') + end end end - - def autocorrect(node) - ->(corrector) { corrector.replace(node.loc.selector, 'be') } - end end end end diff --git a/lib/rubocop/cop/rspec/capybara/current_path_expectation.rb b/lib/rubocop/cop/rspec/capybara/current_path_expectation.rb index c69303161..ec51d2c85 100644 --- a/lib/rubocop/cop/rspec/capybara/current_path_expectation.rb +++ b/lib/rubocop/cop/rspec/capybara/current_path_expectation.rb @@ -24,6 +24,8 @@ module Capybara # expect(page).to have_current_path(/widgets/) # class CurrentPathExpectation < Cop + extend AutoCorrector + MSG = 'Do not set an RSpec expectation on `current_path` in ' \ 'Capybara feature specs - instead, use the ' \ '`have_current_path` matcher on `page`' @@ -47,30 +49,30 @@ class CurrentPathExpectation < Cop def on_send(node) expectation_set_on_current_path(node) do - add_offense(node, location: :selector) + add_offense(node.loc.selector) do |corrector| + next unless node.chained? + + autocorrect(corrector, node) + end end end - def autocorrect(node) - lambda do |corrector| - return unless node.chained? + private - as_is_matcher(node.parent) do |to_sym, matcher_node| - rewrite_expectation(corrector, node, to_sym, matcher_node) - end + def autocorrect(corrector, node) + as_is_matcher(node.parent) do |to_sym, matcher_node| + rewrite_expectation(corrector, node, to_sym, matcher_node) + end - regexp_str_matcher(node.parent) do |to_sym, matcher_node, regexp| - rewrite_expectation(corrector, node, to_sym, matcher_node) - convert_regexp_str_to_literal(corrector, matcher_node, regexp) - end + regexp_str_matcher(node.parent) do |to_sym, matcher_node, regexp| + rewrite_expectation(corrector, node, to_sym, matcher_node) + convert_regexp_str_to_literal(corrector, matcher_node, regexp) end end - private - def rewrite_expectation(corrector, node, to_symbol, matcher_node) current_path_node = node.first_argument - corrector.replace(current_path_node.loc.expression, 'page') + corrector.replace(current_path_node, 'page') corrector.replace(node.parent.loc.selector, 'to') matcher_method = if to_symbol == :to 'have_current_path' @@ -84,7 +86,7 @@ def rewrite_expectation(corrector, node, to_symbol, matcher_node) def convert_regexp_str_to_literal(corrector, matcher_node, regexp_str) str_node = matcher_node.first_argument regexp_expr = Regexp.new(regexp_str).inspect - corrector.replace(str_node.loc.expression, regexp_expr) + corrector.replace(str_node, regexp_expr) end # `have_current_path` with no options will include the querystring @@ -97,7 +99,7 @@ def add_ignore_query_options(corrector, node) return if %i[regexp str].include?(expectation_last_child.type) corrector.insert_after( - expectation_last_child.loc.expression, + expectation_last_child, ', ignore_query: true' ) end diff --git a/lib/rubocop/cop/rspec/capybara/feature_methods.rb b/lib/rubocop/cop/rspec/capybara/feature_methods.rb index f6681fe28..c8e837420 100644 --- a/lib/rubocop/cop/rspec/capybara/feature_methods.rb +++ b/lib/rubocop/cop/rspec/capybara/feature_methods.rb @@ -41,6 +41,8 @@ module Capybara # end # end class FeatureMethods < Cop + extend AutoCorrector + MSG = 'Use `%s` instead of `%s`.' # https://git.io/v7Kwr @@ -71,18 +73,15 @@ def on_block(node) feature_method(node) do |send_node, match| next if enabled?(match) - add_offense( - send_node, - location: :selector, - message: format(MSG, method: match, replacement: MAP[match]) - ) + add_offense(send_node.loc.selector) do |corrector| + corrector.replace(send_node.loc.selector, MAP[match].to_s) + end end end - def autocorrect(node) - lambda do |corrector| - corrector.replace(node.loc.selector, MAP[node.method_name].to_s) - end + def message(range) + name = range.source.to_sym + format(MSG, method: name, replacement: MAP[name]) end private diff --git a/lib/rubocop/cop/rspec/context_method.rb b/lib/rubocop/cop/rspec/context_method.rb index 23061ad3f..823947236 100644 --- a/lib/rubocop/cop/rspec/context_method.rb +++ b/lib/rubocop/cop/rspec/context_method.rb @@ -24,6 +24,8 @@ module RSpec # # ... # end class ContextMethod < Cop + extend AutoCorrector + MSG = 'Use `describe` for testing methods.' def_node_matcher :context_method, <<-PATTERN @@ -32,13 +34,9 @@ class ContextMethod < Cop def on_block(node) context_method(node) do |context| - add_offense(context) - end - end - - def autocorrect(node) - lambda do |corrector| - corrector.replace(node.parent.loc.selector, 'describe') + add_offense(context) do |corrector| + corrector.replace(node.send_node.loc.selector, 'describe') + end end end diff --git a/lib/rubocop/cop/rspec/cop.rb b/lib/rubocop/cop/rspec/cop.rb index 1c8cf0086..d9f09d4f1 100644 --- a/lib/rubocop/cop/rspec/cop.rb +++ b/lib/rubocop/cop/rspec/cop.rb @@ -17,7 +17,7 @@ module RSpec # # Patterns: # # - '_test.rb$' # # - '(?:^|/)test/' - class Cop < ::RuboCop::Cop::Cop + class Cop < ::RuboCop::Cop::Base include RuboCop::RSpec::Language include RuboCop::RSpec::Language::NodePattern diff --git a/lib/rubocop/cop/rspec/described_class.rb b/lib/rubocop/cop/rspec/described_class.rb index cbe453858..222b8f9db 100644 --- a/lib/rubocop/cop/rspec/described_class.rb +++ b/lib/rubocop/cop/rspec/described_class.rb @@ -55,6 +55,7 @@ module RSpec # end # class DescribedClass < Cop + extend AutoCorrector include ConfigurableEnforcedStyle DESCRIBED_CLASS = 'described_class' @@ -85,22 +86,24 @@ def on_block(node) return unless body find_usage(body) do |match| - add_offense(match, message: message(match.const_name)) + msg = message(match.const_name) + add_offense(match, message: msg) do |corrector| + autocorrect(corrector, match) + end end end - def autocorrect(node) + private + + def autocorrect(corrector, match) replacement = if style == :described_class DESCRIBED_CLASS else @described_class.const_name end - lambda do |corrector| - corrector.replace(node.loc.expression, replacement) - end - end - private + corrector.replace(match, replacement) + end def find_usage(node, &block) yield(node) if offensive?(node) diff --git a/lib/rubocop/cop/rspec/dialect.rb b/lib/rubocop/cop/rspec/dialect.rb index 137359483..1ad00ff9f 100644 --- a/lib/rubocop/cop/rspec/dialect.rb +++ b/lib/rubocop/cop/rspec/dialect.rb @@ -42,6 +42,7 @@ module RSpec # # ... # end class Dialect < Cop + extend AutoCorrector include MethodPreference MSG = 'Prefer `%s` over `%s`.' @@ -52,24 +53,16 @@ def on_send(node) return unless rspec_method?(node) return unless preferred_methods[node.method_name] - add_offense(node) - end + msg = format(MSG, prefer: preferred_method(node.method_name), + current: node.method_name) - def autocorrect(node) - lambda do |corrector| + add_offense(node, message: msg) do |corrector| current = node.loc.selector preferred = preferred_method(current.source) corrector.replace(current, preferred) end end - - private - - def message(node) - format(MSG, prefer: preferred_method(node.method_name), - current: node.method_name) - end end end end diff --git a/lib/rubocop/cop/rspec/empty_hook.rb b/lib/rubocop/cop/rspec/empty_hook.rb index ea5618032..4e62cd95f 100644 --- a/lib/rubocop/cop/rspec/empty_hook.rb +++ b/lib/rubocop/cop/rspec/empty_hook.rb @@ -23,6 +23,7 @@ module RSpec # end # after(:all) { cleanup_feed } class EmptyHook < Cop + extend AutoCorrector include RuboCop::Cop::RangeHelp MSG = 'Empty hook detected.' @@ -33,15 +34,10 @@ class EmptyHook < Cop def on_block(node) empty_hook?(node) do |hook| - add_offense(hook) - end - end - - def autocorrect(node) - lambda do |corrector| - block = node.parent - range = range_with_surrounding_space(range: block.loc.expression) - corrector.remove(range) + add_offense(hook) do |corrector| + range = range_with_surrounding_space(range: node.loc.expression) + corrector.remove(range) + end end end end diff --git a/lib/rubocop/cop/rspec/empty_line_after_example.rb b/lib/rubocop/cop/rspec/empty_line_after_example.rb index 7ddd05632..fb8287470 100644 --- a/lib/rubocop/cop/rspec/empty_line_after_example.rb +++ b/lib/rubocop/cop/rspec/empty_line_after_example.rb @@ -42,6 +42,7 @@ module RSpec # end # class EmptyLineAfterExample < Cop + extend AutoCorrector include RuboCop::RSpec::BlankLineSeparation MSG = 'Add an empty line after `%s`.' @@ -52,9 +53,10 @@ def on_block(node) return if allowed_one_liner?(node) missing_separating_line(node) do |location| - add_offense(node, - location: location, - message: format(MSG, example: node.method_name)) + msg = format(MSG, example: node.method_name) + add_offense(location, message: msg) do |corrector| + corrector.insert_after(location.end, "\n") + end end end diff --git a/lib/rubocop/cop/rspec/empty_line_after_example_group.rb b/lib/rubocop/cop/rspec/empty_line_after_example_group.rb index 7b87add04..5fe23fc54 100644 --- a/lib/rubocop/cop/rspec/empty_line_after_example_group.rb +++ b/lib/rubocop/cop/rspec/empty_line_after_example_group.rb @@ -24,6 +24,7 @@ module RSpec # end # class EmptyLineAfterExampleGroup < Cop + extend AutoCorrector include RuboCop::RSpec::BlankLineSeparation MSG = 'Add an empty line after `%s`.' @@ -33,11 +34,10 @@ def on_block(node) return if last_child?(node) missing_separating_line(node) do |location| - add_offense( - node, - location: location, - message: format(MSG, example_group: node.method_name) - ) + msg = format(MSG, example_group: node.method_name) + add_offense(location, message: msg) do |corrector| + corrector.insert_after(location.end, "\n") + end end end end diff --git a/lib/rubocop/cop/rspec/empty_line_after_final_let.rb b/lib/rubocop/cop/rspec/empty_line_after_final_let.rb index a059433ae..04a9537ef 100644 --- a/lib/rubocop/cop/rspec/empty_line_after_final_let.rb +++ b/lib/rubocop/cop/rspec/empty_line_after_final_let.rb @@ -17,6 +17,7 @@ module RSpec # # it { does_something } class EmptyLineAfterFinalLet < Cop + extend AutoCorrector include RuboCop::RSpec::BlankLineSeparation MSG = 'Add an empty line after the last `let` block.' @@ -30,7 +31,9 @@ def on_block(node) return if last_child?(latest_let) missing_separating_line(latest_let) do |location| - add_offense(latest_let, location: location) + add_offense(location) do |corrector| + corrector.insert_after(location.end, "\n") + end end end end diff --git a/lib/rubocop/cop/rspec/empty_line_after_hook.rb b/lib/rubocop/cop/rspec/empty_line_after_hook.rb index 678534317..1b5f49881 100644 --- a/lib/rubocop/cop/rspec/empty_line_after_hook.rb +++ b/lib/rubocop/cop/rspec/empty_line_after_hook.rb @@ -34,6 +34,7 @@ module RSpec # it { does_something } # class EmptyLineAfterHook < Cop + extend AutoCorrector include RuboCop::RSpec::BlankLineSeparation MSG = 'Add an empty line after `%s`.' @@ -43,11 +44,10 @@ def on_block(node) return if last_child?(node) missing_separating_line(node) do |location| - add_offense( - node, - location: location, - message: format(MSG, hook: node.method_name) - ) + msg = format(MSG, hook: node.method_name) + add_offense(location, message: msg) do |corrector| + corrector.insert_after(location.end, "\n") + end end end end diff --git a/lib/rubocop/cop/rspec/empty_line_after_subject.rb b/lib/rubocop/cop/rspec/empty_line_after_subject.rb index a0f6ed0f5..2b95671fa 100644 --- a/lib/rubocop/cop/rspec/empty_line_after_subject.rb +++ b/lib/rubocop/cop/rspec/empty_line_after_subject.rb @@ -15,6 +15,7 @@ module RSpec # # let(:foo) { bar } class EmptyLineAfterSubject < Cop + extend AutoCorrector include RuboCop::RSpec::BlankLineSeparation MSG = 'Add empty line after `subject`.' @@ -24,7 +25,9 @@ def on_block(node) return if last_child?(node) missing_separating_line(node) do |location| - add_offense(node, location: location) + add_offense(location) do |corrector| + corrector.insert_after(location.end, "\n") + end end end diff --git a/lib/rubocop/cop/rspec/example_wording.rb b/lib/rubocop/cop/rspec/example_wording.rb index 5407544e9..bef638e1d 100644 --- a/lib/rubocop/cop/rspec/example_wording.rb +++ b/lib/rubocop/cop/rspec/example_wording.rb @@ -30,6 +30,8 @@ module RSpec # it 'does things' do # end class ExampleWording < Cop + extend AutoCorrector + MSG_SHOULD = 'Do not use should when describing your tests.' MSG_IT = "Do not repeat 'it' when describing your tests." @@ -53,16 +55,13 @@ def on_block(node) end end - def autocorrect(node) - lambda do |corrector| - corrector.replace(docstring(node), replacement_text(node)) - end - end - private def add_wording_offense(node, message) - add_offense(node, location: docstring(node), message: message) + docstring = docstring(node) + add_offense(docstring, message: message) do |corrector| + corrector.replace(docstring, replacement_text(node)) + end end def docstring(node) diff --git a/lib/rubocop/cop/rspec/expect_actual.rb b/lib/rubocop/cop/rspec/expect_actual.rb index 3bffe2179..d01b28a46 100644 --- a/lib/rubocop/cop/rspec/expect_actual.rb +++ b/lib/rubocop/cop/rspec/expect_actual.rb @@ -17,6 +17,8 @@ module RSpec # expect(name).to eq("John") # class ExpectActual < Cop + extend AutoCorrector + MSG = 'Provide the actual you are testing to `expect(...)`.' SIMPLE_LITERALS = %i[ @@ -55,17 +57,12 @@ class ExpectActual < Cop PATTERN def on_send(node) - expect_literal(node) do |argument| - add_offense(node, location: argument.source_range) - end - end - - def autocorrect(node) - actual, matcher, expected = expect_literal(node) - lambda do |corrector| - return unless SUPPORTED_MATCHERS.include?(matcher) + expect_literal(node) do |actual, matcher, expected| + add_offense(actual.source_range) do |corrector| + next unless SUPPORTED_MATCHERS.include?(matcher) - swap(corrector, actual, expected) + swap(corrector, actual, expected) + end end end diff --git a/lib/rubocop/cop/rspec/expect_change.rb b/lib/rubocop/cop/rspec/expect_change.rb index e2afd909a..7687f9cba 100644 --- a/lib/rubocop/cop/rspec/expect_change.rb +++ b/lib/rubocop/cop/rspec/expect_change.rb @@ -30,6 +30,7 @@ module RSpec # expect { run }.to change { user.reload.name } # class ExpectChange < Cop + extend AutoCorrector include ConfigurableEnforcedStyle MSG_BLOCK = 'Prefer `change(%s, :%s)`.' @@ -51,10 +52,11 @@ def on_send(node) return unless style == :block expect_change_with_arguments(node) do |receiver, message| - add_offense( - node, - message: format(MSG_CALL, obj: receiver, attr: message) - ) + msg = format(MSG_CALL, obj: receiver, attr: message) + add_offense(node, message: msg) do |corrector| + replacement = "change { #{receiver}.#{message} }" + corrector.replace(node, replacement) + end end end @@ -62,37 +64,10 @@ def on_block(node) return unless style == :method_call expect_change_with_block(node) do |receiver, message| - add_offense( - node, - message: format(MSG_BLOCK, obj: receiver, attr: message) - ) - end - end - - def autocorrect(node) - if style == :block - autocorrect_method_call_to_block(node) - else - autocorrect_block_to_method_call(node) - end - end - - private - - def autocorrect_method_call_to_block(node) - lambda do |corrector| - expect_change_with_arguments(node) do |receiver, message| - replacement = "change { #{receiver}.#{message} }" - corrector.replace(node.loc.expression, replacement) - end - end - end - - def autocorrect_block_to_method_call(node) - lambda do |corrector| - expect_change_with_block(node) do |receiver, message| + msg = format(MSG_BLOCK, obj: receiver, attr: message) + add_offense(node, message: msg) do |corrector| replacement = "change(#{receiver}, :#{message})" - corrector.replace(node.loc.expression, replacement) + corrector.replace(node, replacement) end end end diff --git a/lib/rubocop/cop/rspec/expect_in_hook.rb b/lib/rubocop/cop/rspec/expect_in_hook.rb index b0cd8e69b..8bf284ac0 100644 --- a/lib/rubocop/cop/rspec/expect_in_hook.rb +++ b/lib/rubocop/cop/rspec/expect_in_hook.rb @@ -30,8 +30,8 @@ def on_block(node) return if node.body.nil? expectation(node.body) do |expect| - add_offense(expect, location: :selector, - message: message(expect, node)) + add_offense(expect.loc.selector, + message: message(expect, node)) end end diff --git a/lib/rubocop/cop/rspec/expect_output.rb b/lib/rubocop/cop/rspec/expect_output.rb index 9d2e37153..92ec20216 100644 --- a/lib/rubocop/cop/rspec/expect_output.rb +++ b/lib/rubocop/cop/rspec/expect_output.rb @@ -27,7 +27,7 @@ def on_gvasgn(node) name = variable_name[1..-1] return unless name.eql?('stdout') || name.eql?('stderr') - add_offense(node, location: :name, message: format(MSG, name: name)) + add_offense(node.loc.name, message: format(MSG, name: name)) end private diff --git a/lib/rubocop/cop/rspec/factory_bot/attribute_defined_statically.rb b/lib/rubocop/cop/rspec/factory_bot/attribute_defined_statically.rb index b081d0ff7..23afbd8b3 100644 --- a/lib/rubocop/cop/rspec/factory_bot/attribute_defined_statically.rb +++ b/lib/rubocop/cop/rspec/factory_bot/attribute_defined_statically.rb @@ -25,6 +25,8 @@ module FactoryBot # # good # count { 1 } class AttributeDefinedStatically < Cop + extend AutoCorrector + MSG = 'Use a block to declare attribute values.' def_node_matcher :value_matcher, <<-PATTERN @@ -43,20 +45,22 @@ def on_block(node) next unless offensive_receiver?(attribute.receiver, node) next if proc?(attribute) || association?(attribute.first_argument) - add_offense(attribute) + add_offense(attribute) do |corrector| + autocorrect(corrector, attribute) + end end end - def autocorrect(node) + private + + def autocorrect(corrector, node) if node.parenthesized? - autocorrect_replacing_parens(node) + autocorrect_replacing_parens(corrector, node) else - autocorrect_without_parens(node) + autocorrect_without_parens(corrector, node) end end - private - def offensive_receiver?(receiver, node) receiver.nil? || receiver.self_type? || @@ -77,24 +81,20 @@ def proc?(attribute) def_node_matcher :association?, '(hash <(pair (sym :factory) _) ...>)' - def autocorrect_replacing_parens(node) + def autocorrect_replacing_parens(corrector, node) left_braces, right_braces = braces(node) - lambda do |corrector| - corrector.replace(node.location.begin, ' ' + left_braces) - corrector.replace(node.location.end, right_braces) - end + corrector.replace(node.location.begin, ' ' + left_braces) + corrector.replace(node.location.end, right_braces) end - def autocorrect_without_parens(node) + def autocorrect_without_parens(corrector, node) left_braces, right_braces = braces(node) - lambda do |corrector| - argument = node.first_argument - expression = argument.location.expression - corrector.insert_before(expression, left_braces) - corrector.insert_after(expression, right_braces) - end + argument = node.first_argument + expression = argument.location.expression + corrector.insert_before(expression, left_braces) + corrector.insert_after(expression, right_braces) end def braces(node) diff --git a/lib/rubocop/cop/rspec/factory_bot/create_list.rb b/lib/rubocop/cop/rspec/factory_bot/create_list.rb index 11addc723..1c84d4312 100644 --- a/lib/rubocop/cop/rspec/factory_bot/create_list.rb +++ b/lib/rubocop/cop/rspec/factory_bot/create_list.rb @@ -25,6 +25,7 @@ module FactoryBot # # good # 3.times { create :user } class CreateList < Cop + extend AutoCorrector include ConfigurableEnforcedStyle MSG_CREATE_LIST = 'Prefer create_list.' @@ -51,26 +52,19 @@ def on_block(node) return unless n_times_block_without_arg?(node) return unless contains_only_factory?(node.body) - add_offense(node.send_node, message: MSG_CREATE_LIST) + add_offense(node.send_node, message: MSG_CREATE_LIST) do |corrector| + CreateListCorrector.new(node.send_node).call(corrector) + end end def on_send(node) return unless style == :n_times factory_list_call(node) do |_receiver, _factory, count, _| - add_offense( - node, - location: :selector, - message: format(MSG_N_TIMES, number: count) - ) - end - end - - def autocorrect(node) - if style == :create_list - CreateListCorrector.new(node) - else - TimesCorrector.new(node) + message = format(MSG_N_TIMES, number: count) + add_offense(node.loc.selector, message: message) do |corrector| + TimesCorrector.new(node).call(corrector) + end end end @@ -115,7 +109,7 @@ def initialize(node) def call(corrector) replacement = generate_n_times_block(node) - corrector.replace(node.loc.expression, replacement) + corrector.replace(node, replacement) end private @@ -148,7 +142,7 @@ def call(corrector) call_replacement(node) end - corrector.replace(node.loc.expression, replacement) + corrector.replace(node, replacement) end private diff --git a/lib/rubocop/cop/rspec/factory_bot/factory_class_name.rb b/lib/rubocop/cop/rspec/factory_bot/factory_class_name.rb index 3a49f33d9..da2e96728 100644 --- a/lib/rubocop/cop/rspec/factory_bot/factory_class_name.rb +++ b/lib/rubocop/cop/rspec/factory_bot/factory_class_name.rb @@ -20,6 +20,8 @@ module FactoryBot # factory :foo, class: 'Foo' do # end class FactoryClassName < Cop + extend AutoCorrector + MSG = "Pass '%s' string instead of `%s` " \ 'constant.' ALLOWED_CONSTANTS = %w[Hash OpenStruct].freeze @@ -32,13 +34,10 @@ def on_send(node) class_name(node) do |cn| next if allowed?(cn.const_name) - add_offense(cn, message: format(MSG, class_name: cn.const_name)) - end - end - - def autocorrect(node) - lambda do |corrector| - corrector.replace(node.loc.expression, "'#{node.source}'") + msg = format(MSG, class_name: cn.const_name) + add_offense(cn, message: msg) do |corrector| + corrector.replace(cn, "'#{cn.source}'") + end end end diff --git a/lib/rubocop/cop/rspec/hook_argument.rb b/lib/rubocop/cop/rspec/hook_argument.rb index 99411cd90..f62ba0301 100644 --- a/lib/rubocop/cop/rspec/hook_argument.rb +++ b/lib/rubocop/cop/rspec/hook_argument.rb @@ -58,6 +58,7 @@ module RSpec # # ... # end class HookArgument < Cop + extend AutoCorrector include ConfigurableEnforcedStyle IMPLICIT_MSG = 'Omit the default `%p` ' \ @@ -78,18 +79,11 @@ def on_block(node) return check_implicit(method_send) unless scope_name style_detected(scope_name) - add_offense( - method_send, - message: explicit_message(scope_name) - ) - end - end - - def autocorrect(node) - scope = implicit_style? ? '' : "(#{style.inspect})" - - lambda do |corrector| - corrector.replace(argument_range(node), scope) + msg = explicit_message(scope_name) + add_offense(method_send, message: msg) do |corrector| + scope = implicit_style? ? '' : "(#{style.inspect})" + corrector.replace(argument_range(method_send), scope) + end end end @@ -99,11 +93,11 @@ def check_implicit(method_send) style_detected(:implicit) return if implicit_style? - add_offense( - method_send, - location: :selector, - message: format(EXPLICIT_MSG, scope: style) - ) + msg = explicit_message(nil) + add_offense(method_send.loc.selector, message: msg) do |corrector| + scope = "(#{style.inspect})" + corrector.replace(argument_range(method_send), scope) + end end def explicit_message(scope) diff --git a/lib/rubocop/cop/rspec/hooks_before_examples.rb b/lib/rubocop/cop/rspec/hooks_before_examples.rb index 1c924da42..dcaba4ea1 100644 --- a/lib/rubocop/cop/rspec/hooks_before_examples.rb +++ b/lib/rubocop/cop/rspec/hooks_before_examples.rb @@ -24,6 +24,8 @@ module RSpec # end # class HooksBeforeExamples < Cop + extend AutoCorrector + MSG = 'Move `%s` above the examples in the group.' def_node_matcher :example_or_group?, <<-PATTERN @@ -39,15 +41,6 @@ def on_block(node) check_hooks(node.body) if multiline_block?(node.body) end - def autocorrect(node) - lambda do |corrector| - first_example = find_first_example(node.parent) - RuboCop::RSpec::Corrector::MoveNode.new( - node, corrector, processed_source - ).move_before(first_example) - end - end - private def multiline_block?(block) @@ -62,16 +55,22 @@ def check_hooks(node) next if child.sibling_index < first_example.sibling_index next unless hook?(child) - add_offense( - child, - message: format(MSG, hook: child.method_name) - ) + msg = format(MSG, hook: child.method_name) + add_offense(child, message: msg) do |corrector| + autocorrect(corrector, child, first_example) + end end end def find_first_example(node) node.children.find { |sibling| example_or_group?(sibling) } end + + def autocorrect(corrector, node, first_example) + RuboCop::RSpec::Corrector::MoveNode.new( + node, corrector, processed_source + ).move_before(first_example) + end end end end diff --git a/lib/rubocop/cop/rspec/implicit_expect.rb b/lib/rubocop/cop/rspec/implicit_expect.rb index ca3184bf8..d691df2a9 100644 --- a/lib/rubocop/cop/rspec/implicit_expect.rb +++ b/lib/rubocop/cop/rspec/implicit_expect.rb @@ -25,6 +25,7 @@ module RSpec # it { should be_truthy } # class ImplicitExpect < Cop + extend AutoCorrector include ConfigurableEnforcedStyle MSG = 'Prefer `%s` over `%s`.' @@ -54,20 +55,11 @@ def on_send(node) # rubocop:disable Metrics/MethodLength else opposite_style_detected - add_offense( - node, - location: source_range, - message: offense_message(expectation_source) - ) - end - end - - def autocorrect(node) - lambda do |corrector| - offense = offending_expect(node) - replacement = replacement_source(offense.source) - - corrector.replace(offense, replacement) + msg = offense_message(expectation_source) + add_offense(source_range, message: msg) do |corrector| + replacement = replacement_source(expectation_source) + corrector.replace(source_range, replacement) + end end end diff --git a/lib/rubocop/cop/rspec/implicit_subject.rb b/lib/rubocop/cop/rspec/implicit_subject.rb index b3deba158..7af5bc299 100644 --- a/lib/rubocop/cop/rspec/implicit_subject.rb +++ b/lib/rubocop/cop/rspec/implicit_subject.rb @@ -27,6 +27,7 @@ module RSpec # it { expect(subject).to be_truthy } # class ImplicitSubject < Cop + extend AutoCorrector include ConfigurableEnforcedStyle MSG = "Don't use implicit subject." @@ -39,10 +40,14 @@ def on_send(node) return unless implicit_subject?(node) return if valid_usage?(node) - add_offense(node) + add_offense(node) do |corrector| + autocorrect(corrector, node) + end end - def autocorrect(node) + private + + def autocorrect(corrector, node) replacement = 'expect(subject)' if node.method_name == :should replacement += '.to' @@ -50,11 +55,9 @@ def autocorrect(node) replacement += '.not_to' end - ->(corrector) { corrector.replace(node.loc.selector, replacement) } + corrector.replace(node.loc.selector, replacement) end - private - def valid_usage?(node) example = node.ancestors.find { |parent| example?(parent) } return false if example.nil? diff --git a/lib/rubocop/cop/rspec/instance_spy.rb b/lib/rubocop/cop/rspec/instance_spy.rb index 7b80c167a..96a2d407b 100644 --- a/lib/rubocop/cop/rspec/instance_spy.rb +++ b/lib/rubocop/cop/rspec/instance_spy.rb @@ -19,6 +19,8 @@ module RSpec # end # class InstanceSpy < Cop + extend AutoCorrector + MSG = 'Use `instance_spy` when you check your double '\ 'with `have_received`.' @@ -43,22 +45,26 @@ def on_block(node) null_double(node) do |var, receiver| have_received_usage(node) do |expected| - add_offense(receiver) if expected == var + next if expected != var + + add_offense(receiver) do |corrector| + autocorrect(corrector, receiver) + end end end end - def autocorrect(node) - lambda do |corrector| - replacement = 'instance_spy' - corrector.replace(node.loc.selector, replacement) + private - double_source_map = node.parent.loc - as_null_object_range = double_source_map - .dot - .join(double_source_map.selector) - corrector.remove(as_null_object_range) - end + def autocorrect(corrector, node) + replacement = 'instance_spy' + corrector.replace(node.loc.selector, replacement) + + double_source_map = node.parent.loc + as_null_object_range = double_source_map + .dot + .join(double_source_map.selector) + corrector.remove(as_null_object_range) end end end diff --git a/lib/rubocop/cop/rspec/invalid_predicate_matcher.rb b/lib/rubocop/cop/rspec/invalid_predicate_matcher.rb index c0944de78..011ea0027 100644 --- a/lib/rubocop/cop/rspec/invalid_predicate_matcher.rb +++ b/lib/rubocop/cop/rspec/invalid_predicate_matcher.rb @@ -24,7 +24,8 @@ class InvalidPredicateMatcher < Cop def on_send(node) invalid_predicate_matcher?(node) do |predicate| - add_offense(predicate) + add_offense(predicate, + message: format(MSG, matcher: predicate.method_name)) end end @@ -34,10 +35,6 @@ def predicate?(name) name = name.to_s name.start_with?('be_', 'have_') && name.end_with?('?') end - - def message(predicate) - format(MSG, matcher: predicate.method_name) - end end end end diff --git a/lib/rubocop/cop/rspec/it_behaves_like.rb b/lib/rubocop/cop/rspec/it_behaves_like.rb index 6da6f241d..bc219b573 100644 --- a/lib/rubocop/cop/rspec/it_behaves_like.rb +++ b/lib/rubocop/cop/rspec/it_behaves_like.rb @@ -19,6 +19,7 @@ module RSpec # # good # it_should_behave_like 'a foo' class ItBehavesLike < Cop + extend AutoCorrector include ConfigurableEnforcedStyle MSG = 'Prefer `%s` over `%s` when including '\ @@ -28,14 +29,12 @@ class ItBehavesLike < Cop def on_send(node) example_inclusion_offense(node, alternative_style) do - add_offense(node) + add_offense(node) do |corrector| + corrector.replace(node.loc.selector, style.to_s) + end end end - def autocorrect(node) - ->(corrector) { corrector.replace(node.loc.selector, style.to_s) } - end - private def message(_node) diff --git a/lib/rubocop/cop/rspec/leading_subject.rb b/lib/rubocop/cop/rspec/leading_subject.rb index 6176f5c58..9080e9a77 100644 --- a/lib/rubocop/cop/rspec/leading_subject.rb +++ b/lib/rubocop/cop/rspec/leading_subject.rb @@ -32,6 +32,8 @@ module RSpec # it { expect_something_else } # class LeadingSubject < Cop + extend AutoCorrector + MSG = 'Declare `subject` above any other `%s` declarations.' def on_block(node) @@ -43,27 +45,25 @@ def on_block(node) def check_previous_nodes(node) node.parent.each_child_node do |sibling| if offending?(sibling) - add_offense( - node, - message: format(MSG, offending: sibling.method_name) - ) + msg = format(MSG, offending: sibling.method_name) + add_offense(node, message: msg) do |corrector| + autocorrect(corrector, node) + end end break if offending?(sibling) || sibling.equal?(node) end end - def autocorrect(node) - lambda do |corrector| - first_node = find_first_offending_node(node) - RuboCop::RSpec::Corrector::MoveNode.new( - node, corrector, processed_source - ).move_before(first_node) - end - end - private + def autocorrect(corrector, node) + first_node = find_first_offending_node(node) + RuboCop::RSpec::Corrector::MoveNode.new( + node, corrector, processed_source + ).move_before(first_node) + end + def offending?(node) let?(node) || hook?(node) || example?(node) end diff --git a/lib/rubocop/cop/rspec/let_before_examples.rb b/lib/rubocop/cop/rspec/let_before_examples.rb index c41b60add..8df74f412 100644 --- a/lib/rubocop/cop/rspec/let_before_examples.rb +++ b/lib/rubocop/cop/rspec/let_before_examples.rb @@ -31,6 +31,8 @@ module RSpec # expect(some).to be # end class LetBeforeExamples < Cop + extend AutoCorrector + MSG = 'Move `let` before the examples in the group.' def_node_matcher :example_or_group?, <<-PATTERN @@ -46,15 +48,6 @@ def on_block(node) check_let_declarations(node.body) if multiline_block?(node.body) end - def autocorrect(node) - lambda do |corrector| - first_example = find_first_example(node.parent) - RuboCop::RSpec::Corrector::MoveNode.new( - node, corrector, processed_source - ).move_before(first_example) - end - end - private def multiline_block?(block) @@ -67,14 +60,23 @@ def check_let_declarations(node) node.each_child_node do |child| next if child.sibling_index < first_example.sibling_index + next unless let?(child) - add_offense(child) if let?(child) + add_offense(child) do |corrector| + autocorrect(corrector, child, first_example) + end end end def find_first_example(node) node.children.find { |sibling| example_or_group?(sibling) } end + + def autocorrect(corrector, node, first_example) + RuboCop::RSpec::Corrector::MoveNode.new( + node, corrector, processed_source + ).move_before(first_example) + end end end end diff --git a/lib/rubocop/cop/rspec/message_chain.rb b/lib/rubocop/cop/rspec/message_chain.rb index 0e9c020a2..c3fce664e 100644 --- a/lib/rubocop/cop/rspec/message_chain.rb +++ b/lib/rubocop/cop/rspec/message_chain.rb @@ -21,11 +21,12 @@ class MessageChain < Cop PATTERN def on_send(node) - message_chain(node) { add_offense(node, location: :selector) } - end - - def message(node) - format(MSG, method: node.method_name) + message_chain(node) do + add_offense( + node.loc.selector, + message: format(MSG, method: node.method_name) + ) + end end end end diff --git a/lib/rubocop/cop/rspec/message_expectation.rb b/lib/rubocop/cop/rspec/message_expectation.rb index 14b80996e..0a6e4e298 100644 --- a/lib/rubocop/cop/rspec/message_expectation.rb +++ b/lib/rubocop/cop/rspec/message_expectation.rb @@ -42,7 +42,7 @@ def on_send(node) return correct_style_detected if preferred_style?(match) message = format(MSG, style: style) - add_offense(match, location: :selector, message: message) do + add_offense(match.loc.selector, message: message) do opposite_style_detected end end diff --git a/lib/rubocop/cop/rspec/message_spies.rb b/lib/rubocop/cop/rspec/message_spies.rb index 3daf12c69..7f9f7fbc6 100644 --- a/lib/rubocop/cop/rspec/message_spies.rb +++ b/lib/rubocop/cop/rspec/message_spies.rb @@ -48,8 +48,7 @@ def on_send(node) return correct_style_detected if preferred_style?(message_matcher) add_offense( - message_matcher, - location: :selector, + message_matcher.loc.selector, message: error_message(receiver) ) { opposite_style_detected } end diff --git a/lib/rubocop/cop/rspec/multiple_subjects.rb b/lib/rubocop/cop/rspec/multiple_subjects.rb index f6d7d1c25..808d1f256 100644 --- a/lib/rubocop/cop/rspec/multiple_subjects.rb +++ b/lib/rubocop/cop/rspec/multiple_subjects.rb @@ -34,6 +34,7 @@ module RSpec # This is enough of an edge case that people can just move this to # a `before` hook on their own class MultipleSubjects < Cop + extend AutoCorrector include RangeHelp MSG = 'Do not set more than one subject per example group' @@ -44,38 +45,36 @@ def on_block(node) subjects = RuboCop::RSpec::ExampleGroup.new(node).subjects subjects[0...-1].each do |subject| - add_offense(subject) + add_offense(subject) do |corrector| + autocorrect(corrector, subject) + end end end - def autocorrect(node) - return unless node.method_name.equal?(:subject) # Ignore `subject!` + private + + def autocorrect(corrector, subject) + return unless subject.method_name.equal?(:subject) # Ignore `subject!` - if named_subject?(node) - rename_autocorrect(node) + if named_subject?(subject) + rename_autocorrect(corrector, subject) else - remove_autocorrect(node) + remove_autocorrect(corrector, subject) end end - private - def named_subject?(node) node.send_node.arguments? end - def rename_autocorrect(node) - lambda do |corrector| - corrector.replace(node.send_node.loc.selector, 'let') - end + def rename_autocorrect(corrector, node) + corrector.replace(node.send_node.loc.selector, 'let') end - def remove_autocorrect(node) - lambda do |corrector| - range = range_by_whole_lines(node.source_range, - include_final_newline: true) - corrector.remove(range) - end + def remove_autocorrect(corrector, node) + range = range_by_whole_lines(node.source_range, + include_final_newline: true) + corrector.remove(range) end end end diff --git a/lib/rubocop/cop/rspec/named_subject.rb b/lib/rubocop/cop/rspec/named_subject.rb index ac93b7124..efcbd93eb 100644 --- a/lib/rubocop/cop/rspec/named_subject.rb +++ b/lib/rubocop/cop/rspec/named_subject.rb @@ -62,7 +62,7 @@ def on_block(node) return if !rspec_block?(node) || ignored_shared_example?(node) subject_usage(node) do |subject_node| - add_offense(subject_node, location: :selector) + add_offense(subject_node.loc.selector) end end diff --git a/lib/rubocop/cop/rspec/not_to_not.rb b/lib/rubocop/cop/rspec/not_to_not.rb index 9d7959df1..726dbc201 100644 --- a/lib/rubocop/cop/rspec/not_to_not.rb +++ b/lib/rubocop/cop/rspec/not_to_not.rb @@ -16,6 +16,7 @@ module RSpec # expect(false).not_to be_true # end class NotToNot < Cop + extend AutoCorrector include ConfigurableEnforcedStyle MSG = 'Prefer `%s` over `%s`.' @@ -24,14 +25,12 @@ class NotToNot < Cop def on_send(node) not_to_not_offense(node, alternative_style) do - add_offense(node, location: :selector) + add_offense(node.loc.selector) do |corrector| + corrector.replace(node.loc.selector, style.to_s) + end end end - def autocorrect(node) - ->(corrector) { corrector.replace(node.loc.selector, style.to_s) } - end - private def message(_node) diff --git a/lib/rubocop/cop/rspec/predicate_matcher.rb b/lib/rubocop/cop/rspec/predicate_matcher.rb index 8c55458c2..504b72093 100644 --- a/lib/rubocop/cop/rspec/predicate_matcher.rb +++ b/lib/rubocop/cop/rspec/predicate_matcher.rb @@ -14,11 +14,14 @@ module InflectedHelper private def check_inflected(node) - predicate_in_actual?(node) do |predicate| - add_offense( - node, - message: message_inflected(predicate) - ) + predicate_in_actual?(node) do |predicate, to, matcher| + msg = message_inflected(predicate) + add_offense(node, message: msg) do |corrector| + remove_predicate(corrector, predicate) + corrector.replace(node.loc.selector, + true?(to, matcher) ? 'to' : 'not_to') + rewrite_matcher(corrector, predicate, matcher) + end end end @@ -76,17 +79,6 @@ def to_predicate_matcher(name) end # rubocop:enable Metrics/MethodLength - def autocorrect_inflected(node) - predicate_in_actual?(node) do |predicate, to, matcher| - lambda do |corrector| - remove_predicate(corrector, predicate) - corrector.replace(node.loc.selector, - true?(to, matcher) ? 'to' : 'not_to') - rewrite_matcher(corrector, predicate, matcher) - end - end - end - def remove_predicate(corrector, predicate) range = predicate.loc.dot.with( end_pos: predicate.loc.expression.end_pos @@ -123,7 +115,6 @@ def true?(to_symbol, matcher) end # A helper for `explicit` style - # rubocop:disable Metrics/ModuleLength module ExplicitHelper include RuboCop::RSpec::Language extend NodePattern::Macros @@ -143,22 +134,21 @@ def allowed_explicit_matchers end def check_explicit(node) # rubocop:disable Metrics/MethodLength - predicate_matcher_block?(node) do |_actual, matcher| - add_offense( - node, - message: message_explicit(matcher) - ) + predicate_matcher_block?(node) do |actual, matcher| + add_offense(node, message: message_explicit(matcher)) do |corrector| + to_node = node.send_node + corrector_explicit(corrector, to_node, actual, matcher, to_node) + end ignore_node(node.children.first) return end return if part_of_ignored_node?(node) - predicate_matcher?(node) do |_actual, matcher| - add_offense( - node, - message: message_explicit(matcher) - ) + predicate_matcher?(node) do |actual, matcher| + add_offense(node, message: message_explicit(matcher)) do |corrector| + corrector_explicit(corrector, node, actual, matcher, matcher) + end end end @@ -193,31 +183,11 @@ def message_explicit(matcher) matcher_name: matcher.method_name) end - def autocorrect_explicit(node) - autocorrect_explicit_send(node) || - autocorrect_explicit_block(node) - end - - def autocorrect_explicit_send(node) - predicate_matcher?(node) do |actual, matcher| - corrector_explicit(node, actual, matcher, matcher) - end - end - - def autocorrect_explicit_block(node) - predicate_matcher_block?(node) do |actual, matcher| - to_node = node.send_node - corrector_explicit(to_node, actual, matcher, to_node) - end - end - - def corrector_explicit(to_node, actual, matcher, block_child) - lambda do |corrector| - replacement_matcher = replacement_matcher(to_node) - corrector.replace(matcher.loc.expression, replacement_matcher) - move_predicate(corrector, actual, matcher, block_child) - corrector.replace(to_node.loc.selector, 'to') - end + def corrector_explicit(corrector, to_node, actual, matcher, block_child) + replacement_matcher = replacement_matcher(to_node) + corrector.replace(matcher.loc.expression, replacement_matcher) + move_predicate(corrector, actual, matcher, block_child) + corrector.replace(to_node.loc.selector, 'to') end def move_predicate(corrector, actual, matcher, block_child) @@ -261,7 +231,6 @@ def replacement_matcher(node) end end end - # rubocop:enable Metrics/ModuleLength # Prefer using predicate matcher over using predicate method directly. # @@ -301,6 +270,7 @@ def replacement_matcher(node) # # good - the above code is rewritten to it by this cop # expect(foo.something?).to be_truthy class PredicateMatcher < Cop + extend AutoCorrector include ConfigurableEnforcedStyle include InflectedHelper include ExplicitHelper diff --git a/lib/rubocop/cop/rspec/rails/http_status.rb b/lib/rubocop/cop/rspec/rails/http_status.rb index 32a4dbd1e..342675b54 100644 --- a/lib/rubocop/cop/rspec/rails/http_status.rb +++ b/lib/rubocop/cop/rspec/rails/http_status.rb @@ -31,6 +31,7 @@ module Rails # it { is_expected.to have_http_status :error } # class HttpStatus < Cop + extend AutoCorrector include ConfigurableEnforcedStyle def_node_matcher :http_status, <<-PATTERN @@ -42,14 +43,9 @@ def on_send(node) checker = checker_class.new(ast_node) return unless checker.offensive? - add_offense(checker.node, message: checker.message) - end - end - - def autocorrect(node) - lambda do |corrector| - checker = checker_class.new(node) - corrector.replace(node.loc.expression, checker.preferred_style) + add_offense(checker.node, message: checker.message) do |corrector| + corrector.replace(checker.node, checker.preferred_style) + end end end diff --git a/lib/rubocop/cop/rspec/receive_counts.rb b/lib/rubocop/cop/rspec/receive_counts.rb index 184932f3e..c806a3431 100644 --- a/lib/rubocop/cop/rspec/receive_counts.rb +++ b/lib/rubocop/cop/rspec/receive_counts.rb @@ -24,6 +24,8 @@ module RSpec # expect(foo).to receive(:bar).at_most(:twice).times # class ReceiveCounts < Cop + extend AutoCorrector + MSG = 'Use `%s` instead of `%s`.' def_node_matcher :receive_counts, <<-PATTERN @@ -38,27 +40,23 @@ def on_send(node) offending_range = range(node, offending_node) - add_offense( - offending_node, - message: message_for(offending_node, offending_range.source), - location: offending_range - ) + msg = message_for(offending_node, offending_range.source) + add_offense(offending_range, message: msg) do |corrector| + autocorrect(corrector, offending_node, offending_range) + end end end - def autocorrect(node) - lambda do |corrector| - replacement = matcher_for( - node.method_name, - node.first_argument.source.to_i - ) + private - original = range(node.parent, node) - corrector.replace(original, replacement) - end - end + def autocorrect(corrector, node, range) + replacement = matcher_for( + node.method_name, + node.first_argument.source.to_i + ) - private + corrector.replace(range, replacement) + end def message_for(node, source) alternative = matcher_for( diff --git a/lib/rubocop/cop/rspec/receive_never.rb b/lib/rubocop/cop/rspec/receive_never.rb index 1f8975e1e..109cca7b9 100644 --- a/lib/rubocop/cop/rspec/receive_never.rb +++ b/lib/rubocop/cop/rspec/receive_never.rb @@ -14,6 +14,7 @@ module RSpec # expect(foo).not_to receive(:bar) # class ReceiveNever < Cop + extend AutoCorrector MSG = 'Use `not_to receive` instead of `never`.' def_node_search :method_on_stub?, '(send nil? :receive ...)' @@ -21,18 +22,17 @@ class ReceiveNever < Cop def on_send(node) return unless node.method_name == :never && method_on_stub?(node) - add_offense( - node, - location: :selector - ) + add_offense(node.loc.selector) do |corrector| + autocorrect(corrector, node) + end end - def autocorrect(node) - lambda do |corrector| - corrector.replace(node.parent.loc.selector, 'not_to') - range = node.loc.dot.with(end_pos: node.loc.selector.end_pos) - corrector.remove(range) - end + private + + def autocorrect(corrector, node) + corrector.replace(node.parent.loc.selector, 'not_to') + range = node.loc.dot.with(end_pos: node.loc.selector.end_pos) + corrector.remove(range) end end end diff --git a/lib/rubocop/cop/rspec/return_from_stub.rb b/lib/rubocop/cop/rspec/return_from_stub.rb index c7f1b5d64..6484232ed 100644 --- a/lib/rubocop/cop/rspec/return_from_stub.rb +++ b/lib/rubocop/cop/rspec/return_from_stub.rb @@ -34,6 +34,7 @@ module RSpec # allow(Foo).to receive(:bar) { bar.baz } # class ReturnFromStub < Cop + extend AutoCorrector include ConfigurableEnforcedStyle MSG_AND_RETURN = 'Use `and_return` for static values.' @@ -59,24 +60,14 @@ def on_block(node) check_block_body(node) end - def autocorrect(node) - if style == :block - AndReturnCallCorrector.new(node) - else - BlockBodyCorrector.new(node) - end - end - private def check_and_return_call(node) and_return_value(node) do |and_return, args| unless dynamic?(args) - add_offense( - and_return, - location: :selector, - message: MSG_BLOCK - ) + add_offense(and_return.loc.selector, message: MSG_BLOCK) do |corr| + AndReturnCallCorrector.new(and_return).call(corr) + end end end end @@ -84,11 +75,9 @@ def check_and_return_call(node) def check_block_body(block) body = block.body unless dynamic?(body) # rubocop:disable Style/GuardClause - add_offense( - block, - location: :begin, - message: MSG_AND_RETURN - ) + add_offense(block.loc.begin, message: MSG_AND_RETURN) do |corrector| + BlockBodyCorrector.new(block).call(corrector) + end end end @@ -153,7 +142,7 @@ def call(corrector) return if heredoc? corrector.replace( - block.loc.expression, + block, "#{block.send_node.source}.and_return(#{body.source})" ) end diff --git a/lib/rubocop/cop/rspec/scattered_let.rb b/lib/rubocop/cop/rspec/scattered_let.rb index 07c88c6e1..02426458a 100644 --- a/lib/rubocop/cop/rspec/scattered_let.rb +++ b/lib/rubocop/cop/rspec/scattered_let.rb @@ -27,6 +27,8 @@ module RSpec # end # class ScatteredLet < Cop + extend AutoCorrector + MSG = 'Group all let/let! blocks in the example group together.' def on_block(node) @@ -35,15 +37,6 @@ def on_block(node) check_let_declarations(node.body) end - def autocorrect(node) - lambda do |corrector| - first_let = find_first_let(node.parent) - RuboCop::RSpec::Corrector::MoveNode.new( - node, corrector, processed_source - ).move_after(first_let) - end - end - private def check_let_declarations(body) @@ -53,7 +46,11 @@ def check_let_declarations(body) lets.each_with_index do |node, idx| next if node.sibling_index == first_let.sibling_index + idx - add_offense(node) + add_offense(node) do |corrector| + RuboCop::RSpec::Corrector::MoveNode.new( + node, corrector, processed_source + ).move_after(first_let) + end end end diff --git a/lib/rubocop/cop/rspec/shared_context.rb b/lib/rubocop/cop/rspec/shared_context.rb index d1815f0cd..6316ef25e 100644 --- a/lib/rubocop/cop/rspec/shared_context.rb +++ b/lib/rubocop/cop/rspec/shared_context.rb @@ -51,6 +51,8 @@ module RSpec # end # class SharedContext < Cop + extend AutoCorrector + MSG_EXAMPLES = "Use `shared_examples` when you don't "\ 'define context.' @@ -68,22 +70,14 @@ class SharedContext < Cop def on_block(node) context_with_only_examples(node) do - add_shared_item_offense(node.send_node, MSG_EXAMPLES) + add_offense(node.send_node, message: MSG_EXAMPLES) do |corrector| + corrector.replace(node.send_node.loc.selector, 'shared_examples') + end end examples_with_only_context(node) do - add_shared_item_offense(node.send_node, MSG_CONTEXT) - end - end - - def autocorrect(node) - lambda do |corrector| - context_with_only_examples(node.parent) do - corrector.replace(node.loc.selector, 'shared_examples') - end - - examples_with_only_context(node.parent) do - corrector.replace(node.loc.selector, 'shared_context') + add_offense(node.send_node, message: MSG_CONTEXT) do |corrector| + corrector.replace(node.send_node.loc.selector, 'shared_context') end end end @@ -97,13 +91,6 @@ def context_with_only_examples(node) def examples_with_only_context(node) shared_example(node) { yield if context?(node) && !examples?(node) } end - - def add_shared_item_offense(node, message) - add_offense( - node, - message: message - ) - end end end end diff --git a/lib/rubocop/cop/rspec/shared_examples.rb b/lib/rubocop/cop/rspec/shared_examples.rb index 522168a6f..eea12d7c7 100644 --- a/lib/rubocop/cop/rspec/shared_examples.rb +++ b/lib/rubocop/cop/rspec/shared_examples.rb @@ -21,6 +21,8 @@ module RSpec # include_examples 'foo bar baz' # class SharedExamples < Cop + extend AutoCorrector + def_node_matcher :shared_examples, (SharedGroups::ALL + Includes::ALL).send_pattern @@ -30,14 +32,9 @@ def on_send(node) next unless ast_node&.sym_type? checker = Checker.new(ast_node) - add_offense(checker.node, message: checker.message) - end - end - - def autocorrect(node) - lambda do |corrector| - checker = Checker.new(node) - corrector.replace(node.loc.expression, checker.preferred_style) + add_offense(checker.node, message: checker.message) do |corrector| + corrector.replace(checker.node, checker.preferred_style) + end end end diff --git a/lib/rubocop/cop/rspec/single_argument_message_chain.rb b/lib/rubocop/cop/rspec/single_argument_message_chain.rb index 7c4506d33..b8c8a725b 100644 --- a/lib/rubocop/cop/rspec/single_argument_message_chain.rb +++ b/lib/rubocop/cop/rspec/single_argument_message_chain.rb @@ -17,6 +17,8 @@ module RSpec # allow(foo).to receive("bar.baz") # class SingleArgumentMessageChain < Cop + extend AutoCorrector + MSG = 'Use `%s` instead of calling '\ '`%s` with a single argument.' @@ -30,22 +32,23 @@ def on_send(node) message_chain(node) do |arg| return if valid_usage?(arg) - add_offense(node, location: :selector) - end - end + method = node.method_name + msg = format(MSG, recommended: replacement(method), called: method) - def autocorrect(node) - lambda do |corrector| - corrector.replace(node.loc.selector, replacement(node.method_name)) - message_chain(node) do |arg| - autocorrect_hash_arg(corrector, arg) if single_key_hash?(arg) - autocorrect_array_arg(corrector, arg) if arg.array_type? + add_offense(node.loc.selector, message: msg) do |corrector| + autocorrect(corrector, node, method, arg) end end end private + def autocorrect(corrector, node, method, arg) + corrector.replace(node.loc.selector, replacement(method)) + autocorrect_hash_arg(corrector, arg) if single_key_hash?(arg) + autocorrect_array_arg(corrector, arg) if arg.array_type? + end + def valid_usage?(node) return true unless node.literal? || node.array_type? @@ -63,7 +66,7 @@ def single_element_array?(node) def autocorrect_hash_arg(corrector, arg) key, value = *arg.children.first - corrector.replace(arg.loc.expression, key_to_arg(key)) + corrector.replace(arg, key_to_arg(key)) corrector.insert_after(arg.parent.loc.end, ".and_return(#{value.source})") end @@ -71,7 +74,7 @@ def autocorrect_hash_arg(corrector, arg) def autocorrect_array_arg(corrector, arg) value = arg.children.first - corrector.replace(arg.loc.expression, value.source) + corrector.replace(arg, value.source) end def key_to_arg(node) @@ -82,12 +85,6 @@ def key_to_arg(node) def replacement(method) method.equal?(:receive_message_chain) ? 'receive' : 'stub' end - - def message(node) - method = node.method_name - - format(MSG, recommended: replacement(method), called: method) - end end end end diff --git a/lib/rubocop/cop/rspec/yield.rb b/lib/rubocop/cop/rspec/yield.rb index 1d931c978..22d6839e1 100644 --- a/lib/rubocop/cop/rspec/yield.rb +++ b/lib/rubocop/cop/rspec/yield.rb @@ -12,6 +12,7 @@ module RSpec # # good # expect(foo).to be(:bar).and_yield(1) class Yield < Cop + extend AutoCorrector include RangeHelp MSG = 'Use `.and_yield`.' @@ -27,22 +28,24 @@ def on_block(node) block_arg(node.arguments) do |block| if calling_block?(node.body, block) - add_offense(node, location: block_range(node)) - end - end - end + range = block_range(node) - def autocorrect(node) - lambda do |corrector| - node_range = range_with_surrounding_space( - range: block_range(node), side: :left - ) - corrector.replace(node_range, generate_replacement(node.body)) + add_offense(range) do |corrector| + autocorrect(corrector, node, range) + end + end end end private + def autocorrect(corrector, node, range) + corrector.replace( + range_with_surrounding_space(range: range, side: :left), + generate_replacement(node.body) + ) + end + def calling_block?(node, block) if node.begin_type? node.each_child_node.all? { |child| block_call?(child, block) } diff --git a/lib/rubocop/rspec/blank_line_separation.rb b/lib/rubocop/rspec/blank_line_separation.rb index 6b9ce28aa..45375e7c5 100644 --- a/lib/rubocop/rspec/blank_line_separation.rb +++ b/lib/rubocop/rspec/blank_line_separation.rb @@ -32,14 +32,6 @@ def last_child?(node) node.equal?(node.parent.children.last) end - - def autocorrect(node) - lambda do |corrector| - missing_separating_line(node) do |location| - corrector.insert_after(location.end, "\n") - end - end - end end end end diff --git a/rubocop-rspec.gemspec b/rubocop-rspec.gemspec index 48af29b11..fb0299e74 100644 --- a/rubocop-rspec.gemspec +++ b/rubocop-rspec.gemspec @@ -36,7 +36,7 @@ Gem::Specification.new do |spec| 'documentation_uri' => 'https://rubocop-rspec.readthedocs.io/' } - spec.add_runtime_dependency 'rubocop', '>= 0.68.1' + spec.add_runtime_dependency 'rubocop', '>= 0.87.0' spec.add_development_dependency 'rack' spec.add_development_dependency 'rake' diff --git a/spec/rubocop/cop/rspec/expect_actual_spec.rb b/spec/rubocop/cop/rspec/expect_actual_spec.rb index fdb5c8b1d..a2062f066 100644 --- a/spec/rubocop/cop/rspec/expect_actual_spec.rb +++ b/spec/rubocop/cop/rspec/expect_actual_spec.rb @@ -308,12 +308,10 @@ let(:cop_config) { {} } it 'ignores rspec-rails routing specs' do - inspect_source( + expect_no_offenses( 'expect(get: "/foo").to be_routeable', 'spec/routing/foo_spec.rb' ) - - expect(cop.offenses).to be_empty end end end diff --git a/tasks/cops_documentation.rake b/tasks/cops_documentation.rake index 8b06365fb..2f0ced818 100644 --- a/tasks/cops_documentation.rake +++ b/tasks/cops_documentation.rake @@ -40,7 +40,7 @@ task generate_cops_documentation: :yard_for_generate_documentation do ] config = config.for_cop(cop) safe_auto_correct = config.fetch('SafeAutoCorrect', true) - autocorrect = if cop.new.support_autocorrect? + autocorrect = if cop.support_autocorrect? "Yes #{'(Unsafe)' unless safe_auto_correct}" else 'No'