Skip to content

Commit

Permalink
Merge pull request #6170 from rrosenblum/expect_correction
Browse files Browse the repository at this point in the history
Proposed testing feature: expect_correction helper
  • Loading branch information
jonas054 authored Jan 19, 2019
2 parents 9402436 + 9050e40 commit 19d65af
Show file tree
Hide file tree
Showing 10 changed files with 205 additions and 117 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
### Bug fixes

* [#6678](https://github.com/rubocop-hq/rubocop/issues/6678): Fix `Lint/DisjunctiveAssignmentInConstructor` when it finds an empty constructor. ([@rmm5t][])
* Do not attempt to auto-correct mass assignment or optional assignment in `Rails/RelativeDateConstant`. ([@rrosenblum][])
* Fix auto-correction of `Style/WordArray` and `Style/SymbolArray` when all elements are on separate lines and there is a trailing comment after the closing bracket. ([@rrosenblum][])
* Fix an exception that occurs when auto-correcting `Layout/ClosingParenthesesIndentation` when there are no arguments. ([@rrosenblum][])

## 0.63.0 (2019-01-16)

Expand Down
2 changes: 1 addition & 1 deletion lib/rubocop/cop/correctors/percent_literal_corrector.rb
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ def process_lines(node, previous_line_num, base_line_num, source_in_lines)
begin_line_num = previous_line_num - base_line_num + 1
end_line_num = node.first_line - base_line_num + 1
lines = source_in_lines[begin_line_num...end_line_num]
"\n" + lines.join("\n").split(node.source).first
"\n#{(lines.join("\n").split(node.source).first || '')}"
end

def fix_escaped_content(word_node, escape, delimiters)
Expand Down
1 change: 1 addition & 0 deletions lib/rubocop/cop/layout/closing_parenthesis_indentation.rb
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ def check_for_no_elements(node)
# Although there are multiple choices for a correct column,
# select the first one of candidates to determine a specification.
correct_column = candidates.first
@column_delta = correct_column - right_paren.column
add_offense(right_paren,
location: right_paren,
message: message(correct_column,
Expand Down
76 changes: 40 additions & 36 deletions lib/rubocop/cop/rails/relative_date_constant.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,18 +19,15 @@ module Rails
# end
# end
class RelativeDateConstant < Cop
include RangeHelp

MSG = 'Do not assign %<method_name>s to constants as it ' \
'will be evaluated only once.'.freeze

RELATIVE_DATE_METHODS = %i[ago from_now since until].freeze

def on_casgn(node)
_scope, _constant, rhs = *node

# rhs would be nil in a or_asgn node
return unless rhs

check_node(rhs)
relative_date_assignment?(node) do |method_name|
add_offense(node, message: format(MSG, method_name: method_name))
end
end

def on_masgn(node)
Expand All @@ -39,20 +36,29 @@ def on_masgn(node)
return unless rhs && rhs.array_type?

lhs.children.zip(rhs.children).each do |(name, value)|
check_node(value) if name.casgn_type?
next unless name.casgn_type?

relative_date?(value) do |method_name|
add_offense(node,
location: range_between(name.loc.expression.begin_pos,
value.loc.expression.end_pos),
message: format(MSG, method_name: method_name))
end
end
end

def on_or_asgn(node)
lhs, rhs = *node

return unless lhs.casgn_type?

check_node(rhs)
relative_date_or_assignment?(node) do |method_name|
add_offense(node, message: format(MSG, method_name: method_name))
end
end

def autocorrect(node)
_scope, const_name, value = *node
return unless node.casgn_type?

scope, const_name, value = *node
return unless scope.nil?

indent = ' ' * node.loc.column
new_code = ["def self.#{const_name.downcase}",
"#{indent}#{value.source}",
Expand All @@ -62,27 +68,25 @@ def autocorrect(node)

private

def check_node(node)
return unless node.irange_type? ||
node.erange_type? ||
node.send_type?

# for range nodes we need to check both their boundaries
nodes = node.send_type? ? [node] : node.children

nodes.each do |n|
if relative_date_method?(n)
add_offense(node.parent,
message: format(MSG, method_name: n.method_name))
end
end
end

def relative_date_method?(node)
node.send_type? &&
RELATIVE_DATE_METHODS.include?(node.method_name) &&
!node.arguments?
end
def_node_matcher :relative_date_assignment?, <<-PATTERN
{
(casgn _ _ (send _ ${:since :from_now :after :ago :until :before}))
(casgn _ _ ({erange irange} _ (send _ ${:since :from_now :after :ago :until :before})))
(casgn _ _ ({erange irange} (send _ ${:since :from_now :after :ago :until :before}) _))
}
PATTERN

def_node_matcher :relative_date_or_assignment?, <<-PATTERN
(:or_asgn (casgn _ _) (send _ ${:since :from_now :after :ago :until :before}))
PATTERN

def_node_matcher :relative_date?, <<-PATTERN
{
({erange irange} _ (send _ ${:since :from_now :after :ago :until :before}))
({erange irange} (send _ ${:since :from_now :after :ago :until :before}) _)
(send _ ${:since :from_now :after :ago :until :before})
}
PATTERN
end
end
end
Expand Down
2 changes: 2 additions & 0 deletions lib/rubocop/rspec/cop_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ def autocorrect_source_file(source)
end

def autocorrect_source(source, file = nil)
RuboCop::Formatter::DisabledConfigFormatter.config_to_allow_offenses = {}
RuboCop::Formatter::DisabledConfigFormatter.detected_styles = {}
cop.instance_variable_get(:@options)[:auto_correct] = true
processed_source = parse_source(source, file)
_investigate(cop, processed_source)
Expand Down
45 changes: 43 additions & 2 deletions lib/rubocop/rspec/expect_offense.rb
Original file line number Diff line number Diff line change
Expand Up @@ -38,24 +38,65 @@ module RSpec
# 'Avoid chaining a method call on a do...end block.'
# )
#
# Auto-correction can be tested using `expect_correction` after
# `expect_offense`.
#
# @example `expect_offense` and `expect_correction`
#
# expect_offense(<<-RUBY.strip_indent)
# x % 2 == 0
# ^^^^^^^^^^ Replace with `Integer#even?`.
# RUBY
#
# expect_correction(<<-RUBY.strip_indent)
# x.even?
# RUBY
#
# If you do not want to specify an offense then use the
# companion method `expect_no_offenses`. This method is a much
# simpler assertion since it just inspects the source and checks
# that there were no offenses. The `expect_offense` method has
# to do more work by parsing out lines that contain carets.
module ExpectOffense
# rubocop:disable Metrics/AbcSize, Metrics/MethodLength
def expect_offense(source, file = nil)
RuboCop::Formatter::DisabledConfigFormatter
.config_to_allow_offenses = {}
RuboCop::Formatter::DisabledConfigFormatter.detected_styles = {}
cop.instance_variable_get(:@options)[:auto_correct] = true

expected_annotations = AnnotatedSource.parse(source)

if expected_annotations.plain_source == source
raise 'Use expect_no_offenses to assert that no offenses are found'
raise 'Use `expect_no_offenses` to assert that no offenses are found'
end

@processed_source = parse_source(expected_annotations.plain_source,
file)

unless @processed_source.valid_syntax?
raise 'Error parsing example code'
end

inspect_source(expected_annotations.plain_source, file)
_investigate(cop, @processed_source)
actual_annotations =
expected_annotations.with_offense_annotations(cop.offenses)

expect(actual_annotations.to_s).to eq(expected_annotations.to_s)
end
# rubocop:enable Metrics/AbcSize, Metrics/MethodLength

def expect_correction(correction)
unless @processed_source
raise '`expect_correction` must follow `expect_offense`'
end

corrector =
RuboCop::Cop::Corrector.new(@processed_source.buffer, cop.corrections)
new_source = corrector.rewrite

expect(new_source).to eq(correction)
end

def expect_no_offenses(source, file = nil)
inspect_source(source, file)
Expand Down
12 changes: 5 additions & 7 deletions spec/rubocop/cop/internal_affairs/node_type_predicate_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,15 @@
subject(:cop) { described_class.new }

context 'comparison node type check' do
it 'registers an offense' do
expect_offense(<<-RUBY.strip_indent, 'example_cop.rb')
it 'registers an offense and auto-corrects' do
expect_offense(<<-RUBY.strip_indent)
node.type == :send
^^^^^^^^^^^^^^^^^^ Use `#send_type?` to check node type.
RUBY
end

it 'auto-corrects' do
new_source = autocorrect_source('node.type == :send')

expect(new_source).to eq('node.send_type?')
expect_correction(<<-RUBY.strip_indent)
node.send_type?
RUBY
end
end

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,11 @@
)
^ Indent `)` to column 0 (not 2)
RUBY

expect_correction(<<-RUBY.strip_indent)
foo = some_method(
)
RUBY
end
end
end
Expand Down
Loading

0 comments on commit 19d65af

Please sign in to comment.