diff --git a/CHANGELOG.md b/CHANGELOG.md index c6dbe8c..9ca6eaf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ * [#140](https://github.com/rubocop-hq/rubocop-performance/pull/140): Add new `Performance/CollectionLiteralInLoop` cop. ([@fatkodima][]) * [#137](https://github.com/rubocop-hq/rubocop-performance/pull/137): Add new `Performance/Sum` cop. ([@fatkodima][]) +### Changes + +* [#154](https://github.com/rubocop-hq/rubocop-performance/pull/154): Require RuboCop 0.87 or higher. ([@koic][]) + ## 1.7.1 (2020-07-18) ### Bug fixes diff --git a/rubocop-performance.gemspec b/rubocop-performance.gemspec index 39e9c83..1997851 100644 --- a/rubocop-performance.gemspec +++ b/rubocop-performance.gemspec @@ -29,6 +29,6 @@ Gem::Specification.new do |s| 'bug_tracker_uri' => 'https://github.com/rubocop-hq/rubocop-performance/issues' } - s.add_runtime_dependency('rubocop', '>= 0.82.0') + s.add_runtime_dependency('rubocop', '>= 0.87.0') s.add_development_dependency('simplecov') end diff --git a/spec/rubocop/cop/performance/casecmp_spec.rb b/spec/rubocop/cop/performance/casecmp_spec.rb index 8ab7256..31c655d 100644 --- a/spec/rubocop/cop/performance/casecmp_spec.rb +++ b/spec/rubocop/cop/performance/casecmp_spec.rb @@ -4,114 +4,170 @@ subject(:cop) { described_class.new } shared_examples 'selectors' do |selector| - it "autocorrects str.#{selector} ==" do - new_source = autocorrect_source("str.#{selector} == 'string'") - expect(new_source).to eq "str.casecmp('string').zero?" + it "registers an offense and corrects str.#{selector} ==" do + expect_offense(<<~RUBY, selector: selector) + str.#{selector} == 'string' + ^^^^^{selector}^^^^^^^^^^^^ Use `str.casecmp('string').zero?` instead of `str.#{selector} == 'string'`. + RUBY + + expect_correction(<<~RUBY) + str.casecmp('string').zero? + RUBY end - it "autocorrects str.#{selector} == with parens around arg" do - new_source = autocorrect_source("str.#{selector} == ('string')") - expect(new_source).to eq "str.casecmp('string').zero?" + it "registers an offense and corrects str.#{selector} == with parens around arg" do + expect_offense(<<~RUBY, selector: selector) + str.#{selector} == ('string') + ^^^^^{selector}^^^^^^^^^^^^^^ Use `str.casecmp('string').zero?` instead of `str.#{selector} == ('string')`. + RUBY + + expect_correction(<<~RUBY) + str.casecmp('string').zero? + RUBY end - it "autocorrects str.#{selector} !=" do - new_source = autocorrect_source("str.#{selector} != 'string'") - expect(new_source).to eq "!str.casecmp('string').zero?" + it "registers an offense and corrects str.#{selector} !=" do + expect_offense(<<~RUBY, selector: selector) + str.#{selector} != 'string' + ^^^^^{selector}^^^^^^^^^^^^ Use `str.casecmp('string').zero?` instead of `str.#{selector} != 'string'`. + RUBY + + expect_correction(<<~RUBY) + !str.casecmp('string').zero? + RUBY end - it "autocorrects str.#{selector} != with parens around arg" do - new_source = autocorrect_source("str.#{selector} != ('string')") - expect(new_source).to eq "!str.casecmp('string').zero?" + it "registers an offense and corrects str.#{selector} != with parens around arg" do + expect_offense(<<~RUBY, selector: selector) + str.#{selector} != ('string') + ^^^^^{selector}^^^^^^^^^^^^^^ Use `str.casecmp('string').zero?` instead of `str.#{selector} != ('string')`. + RUBY + + expect_correction(<<~RUBY) + !str.casecmp('string').zero? + RUBY end - it "autocorrects str.#{selector}.eql? without parens" do - new_source = autocorrect_source("str.#{selector}.eql? 'string'") - expect(new_source).to eq "str.casecmp('string').zero?" + it "registers an offense and corrects str.#{selector}.eql? without parens" do + expect_offense(<<~RUBY, selector: selector) + str.#{selector}.eql? 'string' + ^^^^^{selector}^^^^^^^^^^^^^^ Use `str.casecmp('string').zero?` instead of `str.#{selector}.eql? 'string'`. + RUBY + + expect_correction(<<~RUBY) + str.casecmp('string').zero? + RUBY end - it "autocorrects str.#{selector}.eql? with parens" do - new_source = autocorrect_source("str.#{selector}.eql?('string')") - expect(new_source).to eq "str.casecmp('string').zero?" + it "registers an offense and corrects str.#{selector}.eql? with parens" do + expect_offense(<<~RUBY, selector: selector) + str.#{selector}.eql?('string') + ^^^^^{selector}^^^^^^^^^^^^^^^ Use `str.casecmp('string').zero?` instead of `str.#{selector}.eql?('string')`. + RUBY + + expect_correction(<<~RUBY) + str.casecmp('string').zero? + RUBY end - it "autocorrects str.#{selector}.eql? with parens and funny spacing" do - new_source = autocorrect_source("str.#{selector}.eql? ( 'string' )") - expect(new_source).to eq "str.casecmp( 'string' ).zero?" + it "registers an offense and corrects str.#{selector}.eql? with parens and funny spacing" do + expect_offense(<<~RUBY, selector: selector) + str.#{selector}.eql? ( 'string' ) + ^^^^^{selector}^^^^^^^^^^^^^^^^^^ Use `str.casecmp( 'string' ).zero?` instead of `str.#{selector}.eql? ( 'string' )`. + RUBY + + expect_correction(<<~RUBY) + str.casecmp( 'string' ).zero? + RUBY end - it "autocorrects == str.#{selector}" do - new_source = autocorrect_source("'string' == str.#{selector}") - expect(new_source).to eq "str.casecmp('string').zero?" + it "registers an offense and corrects == str.#{selector}" do + expect_offense(<<~RUBY, selector: selector) + 'string' == str.#{selector} + ^^^^^^^^^^^^^^^^^{selector} Use `str.casecmp('string').zero?` instead of `'string' == str.#{selector}`. + RUBY + + expect_correction(<<~RUBY) + str.casecmp('string').zero? + RUBY end - it "autocorrects string with parens == str.#{selector}" do - new_source = autocorrect_source("('string') == str.#{selector}") - expect(new_source).to eq "str.casecmp('string').zero?" + it "registers an offense and corrects string with parens == str.#{selector}" do + expect_offense(<<~RUBY, selector: selector) + ('string') == str.#{selector} + ^^^^^^^^^^^^^^^^^^^{selector} Use `str.casecmp('string').zero?` instead of `('string') == str.#{selector}`. + RUBY + + expect_correction(<<~RUBY) + str.casecmp('string').zero? + RUBY end - it "autocorrects string != str.#{selector}" do - new_source = autocorrect_source("'string' != str.#{selector}") - expect(new_source).to eq "!str.casecmp('string').zero?" + it "registers an offense and corrects string != str.#{selector}" do + expect_offense(<<~RUBY, selector: selector) + 'string' != str.#{selector} + ^^^^^^^^^^^^^^^^^{selector} Use `str.casecmp('string').zero?` instead of `'string' != str.#{selector}`. + RUBY + + expect_correction(<<~RUBY) + !str.casecmp('string').zero? + RUBY end - it 'autocorrects string with parens and funny spacing ' \ + it 'registers an offense and corrects string with parens and funny spacing ' \ "eql? str.#{selector}" do - new_source = autocorrect_source("( 'string' ).eql? str.#{selector}") - expect(new_source).to eq "str.casecmp( 'string' ).zero?" - end + expect_offense(<<~RUBY, selector: selector) + ( 'string' ).eql? str.#{selector} + ^^^^^^^^^^^^^^^^^^^^^^^{selector} Use `str.casecmp( 'string' ).zero?` instead of `( 'string' ).eql? str.#{selector}`. + RUBY - it "autocorrects string.eql? str.#{selector} without parens " do - new_source = autocorrect_source("'string'.eql? str.#{selector}") - expect(new_source).to eq "str.casecmp('string').zero?" + expect_correction(<<~RUBY) + str.casecmp( 'string' ).zero? + RUBY end - it "autocorrects string.eql? str.#{selector} with parens " do - new_source = autocorrect_source("'string'.eql?(str.#{selector})") - expect(new_source).to eq "str.casecmp('string').zero?" - end + it "registers an offense and corrects string.eql? str.#{selector} without parens " do + expect_offense(<<~RUBY, selector: selector) + 'string'.eql? str.#{selector} + ^^^^^^^^^^^^^^^^^^^{selector} Use `str.casecmp('string').zero?` instead of `'string'.eql? str.#{selector}`. + RUBY - it "autocorrects obj.#{selector} == str.#{selector}" do - new_source = autocorrect_source("obj.#{selector} == str.#{selector}") - expect(new_source).to eq 'obj.casecmp(str).zero?' + expect_correction(<<~RUBY) + str.casecmp('string').zero? + RUBY end - it "autocorrects obj.#{selector} eql? str.#{selector}" do - new_source = autocorrect_source("obj.#{selector}.eql? str.#{selector}") - expect(new_source).to eq 'obj.casecmp(str).zero?' - end + it "registers an offense and corrects string.eql? str.#{selector} with parens " do + expect_offense(<<~RUBY, selector: selector) + 'string'.eql?(str.#{selector}) + ^^^^^^^^^^^^^^^^^^^{selector}^ Use `str.casecmp('string').zero?` instead of `'string'.eql?(str.#{selector})`. + RUBY - it "formats the error message correctly for str.#{selector} ==" do - inspect_source("str.#{selector} == 'string'") - expect(cop.highlights).to eq(["str.#{selector} == 'string'"]) - expect(cop.messages).to eq( - [ - "Use `str.casecmp('string').zero?` instead of " \ - "`str.#{selector} == 'string'`." - ] - ) + expect_correction(<<~RUBY) + str.casecmp('string').zero? + RUBY end - it "formats the error message correctly for == str.#{selector}" do - inspect_source("'string' == str.#{selector}") - expect(cop.highlights).to eq(["'string' == str.#{selector}"]) - expect(cop.messages).to eq( - [ - "Use `str.casecmp('string').zero?` instead of " \ - "`'string' == str.#{selector}`." - ] - ) + it "registers an offense and corrects obj.#{selector} == str.#{selector}" do + expect_offense(<<~RUBY, selector: selector) + obj.#{selector} == str.#{selector} + ^^^^^{selector}^^^^^^^^^{selector} Use `obj.casecmp(str).zero?` instead of `obj.#{selector} == str.#{selector}`. + RUBY + + expect_correction(<<~RUBY) + obj.casecmp(str).zero? + RUBY end - it 'formats the error message correctly for ' \ - "obj.#{selector} == str.#{selector}" do - inspect_source("obj.#{selector} == str.#{selector}") - expect(cop.highlights).to eq(["obj.#{selector} == str.#{selector}"]) - expect(cop.messages).to eq( - [ - 'Use `obj.casecmp(str).zero?` instead of ' \ - "`obj.#{selector} == str.#{selector}`." - ] - ) + it "registers an offense and corrects obj.#{selector} eql? str.#{selector}" do + expect_offense(<<~RUBY, selector: selector) + obj.#{selector}.eql? str.#{selector} + ^^^^^{selector}^^^^^^^^^^^{selector} Use `obj.casecmp(str).zero?` instead of `obj.#{selector}.eql? str.#{selector}`. + RUBY + + expect_correction(<<~RUBY) + obj.casecmp(str).zero? + RUBY end it "doesn't report an offense for variable == str.#{selector}" do diff --git a/spec/rubocop/cop/performance/chain_array_allocation_spec.rb b/spec/rubocop/cop/performance/chain_array_allocation_spec.rb index 325b63c..45579a7 100644 --- a/spec/rubocop/cop/performance/chain_array_allocation_spec.rb +++ b/spec/rubocop/cop/performance/chain_array_allocation_spec.rb @@ -11,11 +11,10 @@ def generate_message(method_one, method_two) shared_examples 'map_and_flat' do |method, method_two| it "registers an offense when calling #{method}...#{method_two}" do - inspect_source("[1, 2, 3, 4].#{method} { |e| [e, e] }.#{method_two}") - - expect(cop.messages) - .to eq([generate_message(method, method_two)]) - expect(cop.highlights).to eq([".#{method_two}"]) + expect_offense(<<~RUBY, method: method, method_two: method_two) + [1, 2, 3, 4].#{method} { |e| [e, e] }.#{method_two} + _{method} ^^{method_two} #{generate_message(method, method_two)} + RUBY end end @@ -50,11 +49,14 @@ def generate_message(method_one, method_two) describe 'methods that only return an array with no block' do it 'zip' do # Yes I know this is not valid Ruby - inspect_source('[1, 2, 3, 4].zip {|f| }.uniq') - expect(cop.messages.empty?).to be(true) + expect_no_offenses(<<~RUBY) + [1, 2, 3, 4].zip {|f| }.uniq + RUBY - inspect_source('[1, 2, 3, 4].zip.uniq') - expect(cop.messages.empty?).to be(false) + expect_offense(<<~RUBY) + [1, 2, 3, 4].zip {|f| }.zip.uniq + ^^^^^ Use unchained `zip!` and `uniq!` (followed by `return array` if required) instead of chaining `zip...uniq`. + RUBY end end end diff --git a/spec/rubocop/cop/performance/compare_with_block_spec.rb b/spec/rubocop/cop/performance/compare_with_block_spec.rb index 8eb355b..1c101a3 100644 --- a/spec/rubocop/cop/performance/compare_with_block_spec.rb +++ b/spec/rubocop/cop/performance/compare_with_block_spec.rb @@ -4,100 +4,56 @@ subject(:cop) { described_class.new } shared_examples 'compare with block' do |method| - it "registers an offense for #{method}" do - inspect_source("array.#{method} { |a, b| a.foo <=> b.foo }") - expect(cop.offenses.size).to eq(1) - end - - it "registers an offense for #{method} with [:foo]" do - inspect_source("array.#{method} { |a, b| a[:foo] <=> b[:foo] }") - expect(cop.offenses.size).to eq(1) - end - - it "registers an offense for #{method} with ['foo']" do - inspect_source("array.#{method} { |a, b| a['foo'] <=> b['foo'] }") - expect(cop.offenses.size).to eq(1) - end - - it "registers an offense for #{method} with [1]" do - inspect_source("array.#{method} { |a, b| a[1] <=> b[1] }") - expect(cop.offenses.size).to eq(1) - end - - it 'highlights compare method' do - inspect_source("array.#{method} { |a, b| a.foo <=> b.foo }") - expect(cop.highlights).to eq(["#{method} { |a, b| a.foo <=> b.foo }"]) - end - - it "accepts valid #{method} usage" do - expect_no_offenses("array.#{method} { |a, b| b <=> a }") - end - - it "accepts #{method}_by" do - expect_no_offenses("array.#{method}_by { |a| a.baz }") - end + it "registers an offense and corrects for #{method}" do + expect_offense(<<~RUBY, method: method) + array.#{method} { |a, b| a.foo <=> b.foo } + ^{method}^^^^^^^^^^^^^^^^^^^^^^^^^^^ Use `#{method}_by(&:foo)` instead of `#{method} { |a, b| a.foo <=> b.foo }`. + RUBY - it "autocorrects array.#{method} { |a, b| a.foo <=> b.foo }" do - new_source = - autocorrect_source("array.#{method} { |a, b| a.foo <=> b.foo }") - expect(new_source).to eq "array.#{method}_by(&:foo)" + expect_correction(<<~RUBY) + array.#{method}_by(&:foo) + RUBY end - it "autocorrects array.#{method} { |a, b| a.bar <=> b.bar }" do - new_source = - autocorrect_source("array.#{method} { |a, b| a.bar <=> b.bar }") - expect(new_source).to eq "array.#{method}_by(&:bar)" - end + it "registers an offense and corrects for #{method} with [:foo]" do + expect_offense(<<~RUBY, method: method) + array.#{method} { |a, b| a[:foo] <=> b[:foo] } + ^{method}^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Use `#{method}_by { |a| a[:foo] }` instead of `#{method} { |a, b| a[:foo] <=> b[:foo] }`. + RUBY - it "autocorrects array.#{method} { |x, y| x.foo <=> y.foo }" do - new_source = - autocorrect_source("array.#{method} { |x, y| x.foo <=> y.foo }") - expect(new_source).to eq "array.#{method}_by(&:foo)" + expect_correction(<<~RUBY) + array.#{method}_by { |a| a[:foo] } + RUBY end - it "autocorrects array.#{method} do |a, b| a.foo <=> b.foo end" do - new_source = autocorrect_source(<<~RUBY) - array.#{method} do |a, b| - a.foo <=> b.foo - end + it "registers an offense and corrects for #{method} with ['foo']" do + expect_offense(<<~RUBY, method: method) + array.#{method} { |a, b| a['foo'] <=> b['foo'] } + ^{method}^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Use `#{method}_by { |a| a['foo'] }` instead of `#{method} { |a, b| a['foo'] <=> b['foo'] }`. RUBY - expect(new_source).to eq "array.#{method}_by(&:foo)\n" - end - it "autocorrects array.#{method} { |a, b| a[:foo] <=> b[:foo] }" do - new_source = autocorrect_source( - "array.#{method} { |a, b| a[:foo] <=> b[:foo] }" - ) - expect(new_source).to eq "array.#{method}_by { |a| a[:foo] }" + expect_correction(<<~RUBY) + array.#{method}_by { |a| a['foo'] } + RUBY end - it "autocorrects array.#{method} { |a, b| a['foo'] <=> b['foo'] }" do - new_source = autocorrect_source( - "array.#{method} { |a, b| a['foo'] <=> b['foo'] }" - ) - expect(new_source).to eq "array.#{method}_by { |a| a['foo'] }" - end + it "registers an offense and corrects for #{method} with [1]" do + expect_offense(<<~RUBY, method: method) + array.#{method} { |a, b| a[1] <=> b[1] } + ^{method}^^^^^^^^^^^^^^^^^^^^^^^^^ Use `#{method}_by { |a| a[1] }` instead of `#{method} { |a, b| a[1] <=> b[1] }`. + RUBY - it "autocorrects array.#{method} { |a, b| a[1] <=> b[1] }" do - new_source = autocorrect_source( - "array.#{method} { |a, b| a[1] <=> b[1] }" - ) - expect(new_source).to eq "array.#{method}_by { |a| a[1] }" + expect_correction(<<~RUBY) + array.#{method}_by { |a| a[1] } + RUBY end - it 'formats the error message correctly for ' \ - "array.#{method} { |a, b| a.foo <=> b.foo }" do - inspect_source("array.#{method} { |a, b| a.foo <=> b.foo }") - expect(cop.messages).to eq(["Use `#{method}_by(&:foo)` instead of " \ - "`#{method} { |a, b| a.foo <=> b.foo }`."]) + it "accepts valid #{method} usage" do + expect_no_offenses("array.#{method} { |a, b| b <=> a }") end - it 'formats the error message correctly for ' \ - "array.#{method} { |a, b| a[:foo] <=> b[:foo] }" do - inspect_source("array.#{method} { |a, b| a[:foo] <=> b[:foo] }") - expected = ["Use `#{method}_by { |a| a[:foo] }` instead of " \ - "`#{method} { |a, b| a[:foo] <=> b[:foo] }`."] - expect(cop.messages).to eq(expected) + it "accepts #{method}_by" do + expect_no_offenses("array.#{method}_by { |a| a.baz }") end end diff --git a/spec/rubocop/cop/performance/count_spec.rb b/spec/rubocop/cop/performance/count_spec.rb index aaebb0a..ad483e6 100644 --- a/spec/rubocop/cop/performance/count_spec.rb +++ b/spec/rubocop/cop/performance/count_spec.rb @@ -5,51 +5,45 @@ shared_examples 'selectors' do |selector| it "registers an offense for using array.#{selector}...size" do - inspect_source("[1, 2, 3].#{selector} { |e| e.even? }.size") - - expect(cop.messages) - .to eq(["Use `count` instead of `#{selector}...size`."]) - expect(cop.highlights).to eq(["#{selector} { |e| e.even? }.size"]) + expect_offense(<<~RUBY, selector: selector) + [1, 2, 3].#{selector} { |e| e.even? }.size + ^{selector}^^^^^^^^^^^^^^^^^^^^^ Use `count` instead of `#{selector}...size`. + RUBY end it "registers an offense for using hash.#{selector}...size" do - inspect_source("{a: 1, b: 2, c: 3}.#{selector} { |e| e == :a }.size") - - expect(cop.messages) - .to eq(["Use `count` instead of `#{selector}...size`."]) - expect(cop.highlights).to eq(["#{selector} { |e| e == :a }.size"]) + expect_offense(<<~RUBY, selector: selector) + {a: 1, b: 2, c: 3}.#{selector} { |e| e == :a }.size + ^{selector}^^^^^^^^^^^^^^^^^^^^^ Use `count` instead of `#{selector}...size`. + RUBY end it "registers an offense for using array.#{selector}...length" do - inspect_source("[1, 2, 3].#{selector} { |e| e.even? }.length") - - expect(cop.messages) - .to eq(["Use `count` instead of `#{selector}...length`."]) - expect(cop.highlights).to eq(["#{selector} { |e| e.even? }.length"]) + expect_offense(<<~RUBY, selector: selector) + [1, 2, 3].#{selector} { |e| e.even? }.length + ^{selector}^^^^^^^^^^^^^^^^^^^^^^^ Use `count` instead of `#{selector}...length`. + RUBY end it "registers an offense for using hash.#{selector}...length" do - inspect_source("{a: 1, b: 2}.#{selector} { |e| e == :a }.length") - - expect(cop.messages) - .to eq(["Use `count` instead of `#{selector}...length`."]) - expect(cop.highlights).to eq(["#{selector} { |e| e == :a }.length"]) + expect_offense(<<~RUBY, selector: selector) + {a: 1, b: 2}.#{selector} { |e| e == :a }.length + ^{selector}^^^^^^^^^^^^^^^^^^^^^^^ Use `count` instead of `#{selector}...length`. + RUBY end it "registers an offense for using array.#{selector}...count" do - inspect_source("[1, 2, 3].#{selector} { |e| e.even? }.count") - - expect(cop.messages) - .to eq(["Use `count` instead of `#{selector}...count`."]) - expect(cop.highlights).to eq(["#{selector} { |e| e.even? }.count"]) + expect_offense(<<~RUBY, selector: selector) + [1, 2, 3].#{selector} { |e| e.even? }.count + ^{selector}^^^^^^^^^^^^^^^^^^^^^^ Use `count` instead of `#{selector}...count`. + RUBY end it "registers an offense for using hash.#{selector}...count" do - inspect_source("{a: 1, b: 2}.#{selector} { |e| e == :a }.count") - - expect(cop.messages) - .to eq(["Use `count` instead of `#{selector}...count`."]) - expect(cop.highlights).to eq(["#{selector} { |e| e == :a }.count"]) + expect_offense(<<~RUBY, selector: selector) + {a: 1, b: 2}.#{selector} { |e| e == :a }.count + ^{selector}^^^^^^^^^^^^^^^^^^^^^^ Use `count` instead of `#{selector}...count`. + RUBY end it "allows usage of #{selector}...count with a block on an array" do @@ -65,39 +59,31 @@ end it "registers an offense for #{selector} with params instead of a block" do - inspect_source(<<~RUBY) + expect_offense(<<~RUBY, selector: selector) Data = Struct.new(:value) array = [Data.new(2), Data.new(3), Data.new(2)] puts array.#{selector}(&:value).count + ^{selector}^^^^^^^^^^^^^^^ Use `count` instead of `#{selector}...count`. RUBY - - expect(cop.messages) - .to eq(["Use `count` instead of `#{selector}...count`."]) - expect(cop.highlights).to eq(["#{selector}(&:value).count"]) end it "registers an offense for #{selector}(&:something).count" do - inspect_source("foo.#{selector}(&:something).count") - - expect(cop.messages) - .to eq(["Use `count` instead of `#{selector}...count`."]) - expect(cop.highlights).to eq(["#{selector}(&:something).count"]) + expect_offense(<<~RUBY, selector: selector) + foo.#{selector}(&:something).count + ^{selector}^^^^^^^^^^^^^^^^^^^ Use `count` instead of `#{selector}...count`. + RUBY end it "registers an offense for #{selector}(&:something).count " \ 'when called as an instance method on its own class' do - source = <<~RUBY + expect_offense(<<~RUBY, selector: selector) class A < Array def count(&block) #{selector}(&block).count + ^{selector}^^^^^^^^^^^^^^ Use `count` instead of `#{selector}...count`. end end RUBY - inspect_source(source) - - expect(cop.messages) - .to eq(["Use `count` instead of `#{selector}...count`."]) - expect(cop.highlights).to eq(["#{selector}(&block).count"]) end it "allows usage of #{selector} without getting the size" do @@ -122,7 +108,7 @@ def count(&block) it_behaves_like('selectors', 'select') it_behaves_like('selectors', 'reject') - context 'ActiveRecord select' do + context 'Active Record select' do it 'allows usage of select with a string' do expect_no_offenses("Model.select('field AS field_one').count") end @@ -184,90 +170,114 @@ def count(&block) context 'properly parses non related code' do it 'will not raise an error for Bundler.setup' do - expect { inspect_source('Bundler.setup(:default, :development)') } - .not_to raise_error + expect { inspect_source('Bundler.setup(:default, :development)') }.not_to raise_error end it 'will not raise an error for RakeTask.new' do - expect { inspect_source('RakeTask.new(:spec)') } - .not_to raise_error + expect { inspect_source('RakeTask.new(:spec)') }.not_to raise_error end end context 'autocorrect' do context 'will correct' do it 'select..size to count' do - new_source = autocorrect_source('[1, 2].select { |e| e > 2 }.size') + expect_offense(<<~RUBY) + [1, 2].select { |e| e > 2 }.size + ^^^^^^^^^^^^^^^^^^^^^^^^^ Use `count` instead of `select...size`. + RUBY - expect(new_source).to eq('[1, 2].count { |e| e > 2 }') + expect_correction(<<~RUBY) + [1, 2].count { |e| e > 2 } + RUBY end it 'select..count without a block to count' do - new_source = autocorrect_source('[1, 2].select { |e| e > 2 }.count') + expect_offense(<<~RUBY) + [1, 2].select { |e| e > 2 }.count + ^^^^^^^^^^^^^^^^^^^^^^^^^^ Use `count` instead of `select...count`. + RUBY - expect(new_source).to eq('[1, 2].count { |e| e > 2 }') + expect_correction(<<~RUBY) + [1, 2].count { |e| e > 2 } + RUBY end it 'select..length to count' do - new_source = autocorrect_source('[1, 2].select { |e| e > 2 }.length') + expect_offense(<<~RUBY) + [1, 2].select { |e| e > 2 }.length + ^^^^^^^^^^^^^^^^^^^^^^^^^^^ Use `count` instead of `select...length`. + RUBY - expect(new_source).to eq('[1, 2].count { |e| e > 2 }') + expect_correction(<<~RUBY) + [1, 2].count { |e| e > 2 } + RUBY end it 'select...size when select has parameters' do - source = <<~RUBY + expect_offense(<<~RUBY) Data = Struct.new(:value) array = [Data.new(2), Data.new(3), Data.new(2)] puts array.select(&:value).size + ^^^^^^^^^^^^^^^^^^^^ Use `count` instead of `select...size`. RUBY - new_source = autocorrect_source(source) - - expect(new_source) - .to eq(<<~RUBY) - Data = Struct.new(:value) - array = [Data.new(2), Data.new(3), Data.new(2)] - puts array.count(&:value) - RUBY + expect_correction(<<~RUBY) + Data = Struct.new(:value) + array = [Data.new(2), Data.new(3), Data.new(2)] + puts array.count(&:value) + RUBY end end describe 'will not correct' do it 'reject...size' do - new_source = autocorrect_source('[1, 2].reject { |e| e > 2 }.size') + expect_offense(<<~RUBY) + [1, 2].reject { |e| e > 2 }.size + ^^^^^^^^^^^^^^^^^^^^^^^^^ Use `count` instead of `reject...size`. + RUBY - expect(new_source).to eq('[1, 2].reject { |e| e > 2 }.size') + expect_correction(<<~RUBY) + [1, 2].reject { |e| e > 2 }.size + RUBY end it 'reject...count' do - new_source = autocorrect_source('[1, 2].reject { |e| e > 2 }.count') + expect_offense(<<~RUBY) + [1, 2].reject { |e| e > 2 }.count + ^^^^^^^^^^^^^^^^^^^^^^^^^^ Use `count` instead of `reject...count`. + RUBY - expect(new_source).to eq('[1, 2].reject { |e| e > 2 }.count') + expect_correction(<<~RUBY) + [1, 2].reject { |e| e > 2 }.count + RUBY end it 'reject...length' do - new_source = autocorrect_source('[1, 2].reject { |e| e > 2 }.length') + expect_offense(<<~RUBY) + [1, 2].reject { |e| e > 2 }.length + ^^^^^^^^^^^^^^^^^^^^^^^^^^^ Use `count` instead of `reject...length`. + RUBY - expect(new_source).to eq('[1, 2].reject { |e| e > 2 }.length') + expect_correction(<<~RUBY) + [1, 2].reject { |e| e > 2 }.length + RUBY end it 'select...count when count has a block' do - source = '[1, 2].select { |e| e > 2 }.count { |e| e.even? }' - new_source = autocorrect_source(source) - - expect(new_source).to eq(source) + expect_no_offenses(<<~RUBY) + [1, 2].select { |e| e > 2 }.count { |e| e.even? } + RUBY end it 'reject...size when select has parameters' do - source = <<~RUBY + expect_offense(<<~RUBY) Data = Struct.new(:value) array = [Data.new(2), Data.new(3), Data.new(2)] puts array.reject(&:value).size + ^^^^^^^^^^^^^^^^^^^^ Use `count` instead of `reject...size`. RUBY - new_source = autocorrect_source(source) - - expect(new_source).to eq(source) + expect_no_corrections end end end diff --git a/spec/rubocop/cop/performance/detect_spec.rb b/spec/rubocop/cop/performance/detect_spec.rb index d2a630b..529ea4d 100644 --- a/spec/rubocop/cop/performance/detect_spec.rb +++ b/spec/rubocop/cop/performance/detect_spec.rb @@ -19,76 +19,104 @@ select_methods = %i[select find_all].freeze select_methods.each do |method| - it "registers an offense when first is called on #{method}" do - inspect_source("[1, 2, 3].#{method} { |i| i % 2 == 0 }.first") + it "registers an offense and corrects when first is called on #{method}" do + expect_offense(<<~RUBY, method: method) + [1, 2, 3].#{method} { |i| i % 2 == 0 }.first + ^{method}^^^^^^^^^^^^^^^^^^^^^^^^^ Use `detect` instead of `#{method}.first`. + RUBY - expect(cop.messages) - .to eq(["Use `detect` instead of `#{method}.first`."]) + expect_correction(<<~RUBY) + [1, 2, 3].detect { |i| i % 2 == 0 } + RUBY end it "doesn't register an offense when first(n) is called on #{method}" do expect_no_offenses("[1, 2, 3].#{method} { |i| i % 2 == 0 }.first(n)") end - it "registers an offense when last is called on #{method}" do - inspect_source("[1, 2, 3].#{method} { |i| i % 2 == 0 }.last") + it "registers an offense and corrects when last is called on #{method}" do + expect_offense(<<~RUBY, method: method) + [1, 2, 3].#{method} { |i| i % 2 == 0 }.last + ^{method}^^^^^^^^^^^^^^^^^^^^^^^^ Use `reverse.detect` instead of `#{method}.last`. + RUBY - expect(cop.messages) - .to eq(["Use `reverse.detect` instead of `#{method}.last`."]) + expect_correction(<<~RUBY) + [1, 2, 3].reverse.detect { |i| i % 2 == 0 } + RUBY end it "doesn't register an offense when last(n) is called on #{method}" do expect_no_offenses("[1, 2, 3].#{method} { |i| i % 2 == 0 }.last(n)") end - it "registers an offense when first is called on multiline #{method}" do - inspect_source(<<~RUBY) + it "registers an offense and corrects when first is called on multiline #{method}" do + expect_offense(<<~RUBY, method: method) [1, 2, 3].#{method} do |i| + ^{method}^^^^^^^ Use `detect` instead of `#{method}.first`. i % 2 == 0 end.first RUBY - expect(cop.messages).to eq(["Use `detect` instead of `#{method}.first`."]) + expect_correction(<<~RUBY) + [1, 2, 3].detect do |i| + i % 2 == 0 + end + RUBY end it "registers an offense when last is called on multiline #{method}" do - inspect_source(<<~RUBY) + expect_offense(<<~RUBY, method: method) [1, 2, 3].#{method} do |i| + ^{method}^^^^^^^ Use `reverse.detect` instead of `#{method}.last`. i % 2 == 0 end.last RUBY - expect(cop.messages) - .to eq(["Use `reverse.detect` instead of `#{method}.last`."]) + expect_correction(<<~RUBY) + [1, 2, 3].reverse.detect do |i| + i % 2 == 0 + end + RUBY end it "registers an offense when first is called on #{method} short syntax" do - inspect_source("[1, 2, 3].#{method}(&:even?).first") + expect_offense(<<~RUBY, method: method) + [1, 2, 3].#{method}(&:even?).first + ^{method}^^^^^^^^^^^^^^^ Use `detect` instead of `#{method}.first`. + RUBY - expect(cop.messages).to eq(["Use `detect` instead of `#{method}.first`."]) + expect_correction(<<~RUBY) + [1, 2, 3].detect(&:even?) + RUBY end it "registers an offense when last is called on #{method} short syntax" do - inspect_source("[1, 2, 3].#{method}(&:even?).last") + expect_offense(<<~RUBY, method: method) + [1, 2, 3].#{method}(&:even?).last + ^{method}^^^^^^^^^^^^^^ Use `reverse.detect` instead of `#{method}.last`. + RUBY - expect(cop.messages) - .to eq(["Use `reverse.detect` instead of `#{method}.last`."]) + expect_correction(<<~RUBY) + [1, 2, 3].reverse.detect(&:even?) + RUBY end - it "registers an offense when #{method} is called" \ - 'on `lazy` without receiver' do - inspect_source("lazy.#{method}(&:even?).first") + it "registers an offense when #{method} is called on `lazy` without receiver" do + expect_offense(<<~RUBY, method: method) + lazy.#{method}(&:even?).first + ^{method}^^^^^^^^^^^^^^^ Use `detect` instead of `#{method}.first`. + RUBY - expect(cop.messages).to eq(["Use `detect` instead of `#{method}.first`."]) + expect_correction(<<~RUBY) + lazy.detect(&:even?) + RUBY end - it "does not register an offense when #{method} is used " \ - 'without first or last' do + it "does not register an offense when #{method} is used without first or last" do expect_no_offenses("[1, 2, 3].#{method} { |i| i % 2 == 0 }") end - it "does not register an offense when #{method} is called" \ - 'without block or args' do + it "does not register an offense when #{method} is called without block or args" do expect_no_offenses("adapter.#{method}.first") end @@ -97,8 +125,7 @@ expect_no_offenses("adapter.#{method}('something').first") end - it "does not register an offense when #{method} is called" \ - 'on lazy enumerable' do + it "does not register an offense when #{method} is called on lazy enumerable" do expect_no_offenses("adapter.lazy.#{method} { 'something' }.first") end end @@ -113,97 +140,59 @@ let(:collection_method) { preferred_method } select_methods.each do |method| - it "corrects #{method}.first to #{preferred_method} (with block)" do - source = "[1, 2, 3].#{method} { |i| i % 2 == 0 }.first" - - new_source = autocorrect_source(source) - - expect(new_source) - .to eq("[1, 2, 3].#{preferred_method} { |i| i % 2 == 0 }") - end - - it "corrects #{method}.last to reverse.#{preferred_method} " \ - '(with block)' do - source = "[1, 2, 3].#{method} { |i| i % 2 == 0 }.last" - - new_source = autocorrect_source(source) - - expect(new_source) - .to eq("[1, 2, 3].reverse.#{preferred_method} { |i| i % 2 == 0 }") - end - - it "corrects #{method}.first to #{preferred_method} (short syntax)" do - source = "[1, 2, 3].#{method}(&:even?).first" - - new_source = autocorrect_source(source) - - expect(new_source).to eq("[1, 2, 3].#{preferred_method}(&:even?)") - end - - it "corrects #{method}.last to reverse.#{preferred_method} " \ - '(short syntax)' do - source = "[1, 2, 3].#{method}(&:even?).last" - - new_source = autocorrect_source(source) - - expect(new_source) - .to eq("[1, 2, 3].reverse.#{preferred_method}(&:even?)") - end - it "corrects #{method}.first to #{preferred_method} (multiline)" do - source = <<~RUBY + expect_offense(<<~RUBY, method: method) [1, 2, 3].#{method} do |i| + ^{method}^^^^^^^ Use `#{preferred_method}` instead of `#{method}.first`. i % 2 == 0 end.first RUBY - new_source = autocorrect_source(source) - expect(new_source).to eq(<<~RUBY) + expect_correction(<<~RUBY) [1, 2, 3].#{preferred_method} do |i| i % 2 == 0 end RUBY end - it "corrects #{method}.last to reverse.#{preferred_method} " \ - '(multiline)' do - source = <<~RUBY + it "corrects #{method}.last to reverse.#{preferred_method} (multiline)" do + expect_offense(<<~RUBY, method: method) [1, 2, 3].#{method} do |i| + ^{method}^^^^^^^ Use `reverse.#{preferred_method}` instead of `#{method}.last`. i % 2 == 0 end.last RUBY - new_source = autocorrect_source(source) - - expect(new_source) - .to eq(<<~RUBY) - [1, 2, 3].reverse.#{preferred_method} do |i| - i % 2 == 0 - end - RUBY + + expect_correction(<<~RUBY) + [1, 2, 3].reverse.#{preferred_method} do |i| + i % 2 == 0 + end + RUBY end - it "corrects multiline #{method} to #{preferred_method} " \ - "with 'first' on the last line" do - source = <<~RUBY + it "corrects multiline #{method} to #{preferred_method} with 'first' on the last line" do + expect_offense(<<~RUBY, method: method) [1, 2, 3].#{method} { true } + ^{method}^^^^^^^^^ Use `#{preferred_method}` instead of `#{method}.first`. .first['x'] RUBY - new_source = autocorrect_source(source) - expect(new_source) - .to eq("[1, 2, 3].#{preferred_method} { true }['x']\n") + expect_correction(<<~RUBY) + [1, 2, 3].#{preferred_method} { true }['x'] + RUBY end it "corrects multiline #{method} to #{preferred_method} " \ "with 'first' on the last line (short syntax)" do - source = <<~RUBY + expect_offense(<<~RUBY, method: method) [1, 2, 3].#{method}(&:blank?) + ^{method}^^^^^^^^^^ Use `#{preferred_method}` instead of `#{method}.first`. .first['x'] RUBY - new_source = autocorrect_source(source) - expect(new_source) - .to eq("[1, 2, 3].#{preferred_method}(&:blank?)['x']\n") + expect_correction(<<~RUBY) + [1, 2, 3].#{preferred_method}(&:blank?)['x'] + RUBY end end end diff --git a/spec/rubocop/cop/performance/double_start_end_with_spec.rb b/spec/rubocop/cop/performance/double_start_end_with_spec.rb index e1698c8..fea70e4 100644 --- a/spec/rubocop/cop/performance/double_start_end_with_spec.rb +++ b/spec/rubocop/cop/performance/double_start_end_with_spec.rb @@ -15,24 +15,15 @@ context 'two #start_with? calls' do context 'with the same receiver' do context 'all parameters of the second call are pure' do - let(:source) { 'x.start_with?(a, b) || x.start_with?("c", D)' } - - it 'registers an offense' do - inspect_source(source) - expect(cop.offenses.size).to eq(1) - expect(cop.offenses.first.message).to eq( - 'Use `x.start_with?(a, b, "c", D)` instead of ' \ - '`x.start_with?(a, b) || x.start_with?("c", D)`.' - ) - expect(cop.highlights).to eq( - ['x.start_with?(a, b) || x.start_with?("c", D)'] - ) - end - - it 'corrects to a single start_with?' do - new_source = autocorrect_source(source) + it 'registers an offense and corrects' do + expect_offense(<<~RUBY) + x.start_with?(a, b) || x.start_with?("c", D) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Use `x.start_with?(a, b, "c", D)` instead of `x.start_with?(a, b) || x.start_with?("c", D)`. + RUBY - expect(new_source).to eq('x.start_with?(a, b, "c", D)') + expect_correction(<<~RUBY) + x.start_with?(a, b, "c", D) + RUBY end end @@ -53,24 +44,15 @@ context 'two #end_with? calls' do context 'with the same receiver' do context 'all parameters of the second call are pure' do - let(:source) { 'x.end_with?(a, b) || x.end_with?("c", D)' } - - it 'registers an offense' do - inspect_source(source) - expect(cop.offenses.size).to eq(1) - expect(cop.offenses.first.message).to eq( - 'Use `x.end_with?(a, b, "c", D)` instead of ' \ - '`x.end_with?(a, b) || x.end_with?("c", D)`.' - ) - expect(cop.highlights).to eq( - ['x.end_with?(a, b) || x.end_with?("c", D)'] - ) - end - - it 'corrects to a single end_with?' do - new_source = autocorrect_source(source) + it 'registers an offense and corrects' do + expect_offense(<<~RUBY) + x.end_with?(a, b) || x.end_with?("c", D) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Use `x.end_with?(a, b, "c", D)` instead of `x.end_with?(a, b) || x.end_with?("c", D)`. + RUBY - expect(new_source).to eq('x.end_with?(a, b, "c", D)') + expect_correction(<<~RUBY) + x.end_with?(a, b, "c", D) + RUBY end end @@ -119,22 +101,15 @@ context 'two #start_with? calls' do context 'with the same receiver' do context 'all parameters of the second call are pure' do - let(:source) { 'x.start_with?(a, b) || x.start_with?("c", D)' } - - it 'registers an offense' do - inspect_source(source) - expect(cop.offenses.size).to eq(1) - expect(cop.offenses.first.message) - .to eq('Use `x.start_with?(a, b, "c", D)` instead of ' \ - '`x.start_with?(a, b) || x.start_with?("c", D)`.') - expect(cop.highlights) - .to eq(['x.start_with?(a, b) || x.start_with?("c", D)']) - end - - it 'corrects to a single start_with?' do - new_source = autocorrect_source(source) + it 'registers an offense and corrects' do + expect_offense(<<~RUBY) + x.start_with?(a, b) || x.start_with?("c", D) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Use `x.start_with?(a, b, "c", D)` instead of `x.start_with?(a, b) || x.start_with?("c", D)`. + RUBY - expect(new_source).to eq('x.start_with?(a, b, "c", D)') + expect_correction(<<~RUBY) + x.start_with?(a, b, "c", D) + RUBY end end end @@ -143,22 +118,15 @@ context 'two #end_with? calls' do context 'with the same receiver' do context 'all parameters of the second call are pure' do - let(:source) { 'x.end_with?(a, b) || x.end_with?("c", D)' } - - it 'registers an offense' do - inspect_source(source) - expect(cop.offenses.size).to eq(1) - expect(cop.offenses.first.message) - .to eq('Use `x.end_with?(a, b, "c", D)` instead of ' \ - '`x.end_with?(a, b) || x.end_with?("c", D)`.') - expect(cop.highlights) - .to eq(['x.end_with?(a, b) || x.end_with?("c", D)']) - end - - it 'corrects to a single end_with?' do - new_source = autocorrect_source(source) + it 'registers an offense and corrects' do + expect_offense(<<~RUBY) + x.end_with?(a, b) || x.end_with?("c", D) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Use `x.end_with?(a, b, "c", D)` instead of `x.end_with?(a, b) || x.end_with?("c", D)`. + RUBY - expect(new_source).to eq('x.end_with?(a, b, "c", D)') + expect_correction(<<~RUBY) + x.end_with?(a, b, "c", D) + RUBY end end end @@ -167,24 +135,15 @@ context 'two #starts_with? calls' do context 'with the same receiver' do context 'all parameters of the second call are pure' do - let(:source) { 'x.starts_with?(a, b) || x.starts_with?("c", D)' } - - it 'registers an offense' do - inspect_source(source) - expect(cop.offenses.size).to eq(1) - expect(cop.offenses.first.message).to eq( - 'Use `x.starts_with?(a, b, "c", D)` instead of ' \ - '`x.starts_with?(a, b) || x.starts_with?("c", D)`.' - ) - expect(cop.highlights).to eq( - ['x.starts_with?(a, b) || x.starts_with?("c", D)'] - ) - end - - it 'corrects to a single starts_with?' do - new_source = autocorrect_source(source) + it 'registers an offense and corrects' do + expect_offense(<<~RUBY) + x.starts_with?(a, b) || x.starts_with?("c", D) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Use `x.starts_with?(a, b, "c", D)` instead of `x.starts_with?(a, b) || x.starts_with?("c", D)`. + RUBY - expect(new_source).to eq('x.starts_with?(a, b, "c", D)') + expect_correction(<<~RUBY) + x.starts_with?(a, b, "c", D) + RUBY end end @@ -207,24 +166,15 @@ context 'two #ends_with? calls' do context 'with the same receiver' do context 'all parameters of the second call are pure' do - let(:source) { 'x.ends_with?(a, b) || x.ends_with?("c", D)' } - - it 'registers an offense' do - inspect_source(source) - expect(cop.offenses.size).to eq(1) - expect(cop.offenses.first.message).to eq( - 'Use `x.ends_with?(a, b, "c", D)` instead of ' \ - '`x.ends_with?(a, b) || x.ends_with?("c", D)`.' - ) - expect(cop.highlights).to eq( - ['x.ends_with?(a, b) || x.ends_with?("c", D)'] - ) - end - - it 'corrects to a single ends_with?' do - new_source = autocorrect_source(source) + it 'registers an offense and corrects' do + expect_offense(<<~RUBY) + x.ends_with?(a, b) || x.ends_with?("c", D) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Use `x.ends_with?(a, b, "c", D)` instead of `x.ends_with?(a, b) || x.ends_with?("c", D)`. + RUBY - expect(new_source).to eq('x.ends_with?(a, b, "c", D)') + expect_correction(<<~RUBY) + x.ends_with?(a, b, "c", D) + RUBY end end diff --git a/spec/rubocop/cop/performance/end_with_spec.rb b/spec/rubocop/cop/performance/end_with_spec.rb index fa64b08..5b8382c 100644 --- a/spec/rubocop/cop/performance/end_with_spec.rb +++ b/spec/rubocop/cop/performance/end_with_spec.rb @@ -6,56 +6,116 @@ let(:cop_config) { { 'SafeMultiline' => safe_multiline } } shared_examples 'different match methods' do |method| - it "autocorrects str#{method} /abc\\z/" do - new_source = autocorrect_source("str#{method} /abc\\z/") - expect(new_source).to eq "str.end_with?('abc')" + it "registers an offense and corrects str#{method} /abc\\z/" do + expect_offense(<<~RUBY, method: method) + str#{method} /abc\\z/ + ^^^^{method}^^^^^^^^ Use `String#end_with?` instead of a regex match anchored to the end of the string. + RUBY + + expect_correction(<<~RUBY) + str.end_with?('abc') + RUBY end - it "autocorrects /abc\\z/#{method} str" do - new_source = autocorrect_source("/abc\\z/#{method} str") - expect(new_source).to eq "str.end_with?('abc')" + it "registers an offense and corrects /abc\\z/#{method} str" do + expect_offense(<<~RUBY, method: method) + /abc\\z/#{method} str + ^^^^^^^^{method}^^^^ Use `String#end_with?` instead of a regex match anchored to the end of the string. + RUBY + + expect_correction(<<~RUBY) + str.end_with?('abc') + RUBY end - it "autocorrects str#{method} /abc$/" do - new_source = autocorrect_source("str#{method} /abc$/") - expect(new_source).to eq "str.end_with?('abc')" + it "registers an offense and corrects str#{method} /abc$/" do + expect_offense(<<~RUBY, method: method) + str#{method} /abc$/ + ^^^^{method}^^^^^^^ Use `String#end_with?` instead of a regex match anchored to the end of the string. + RUBY + + expect_correction(<<~RUBY) + str.end_with?('abc') + RUBY end - it "autocorrects /abc$/#{method} str" do - new_source = autocorrect_source("/abc$/#{method} str") - expect(new_source).to eq "str.end_with?('abc')" + it "registers an offense and corrects /abc$/#{method} str" do + expect_offense(<<~RUBY, method: method) + /abc$/#{method} str + ^^^^^^{method}^^^^^ Use `String#end_with?` instead of a regex match anchored to the end of the string. + RUBY + + expect_correction(<<~RUBY) + str.end_with?('abc') + RUBY end - it "autocorrects str#{method} /\\n\\z/" do - new_source = autocorrect_source("str#{method} /\\n\\z/") - expect(new_source).to eq 'str.end_with?("\n")' + it "registers an offense and corrects str#{method} /\\n\\z/" do + expect_offense(<<~RUBY, method: method) + str#{method} /\\n\\z/ + ^^^^{method}^^^^^^^ Use `String#end_with?` instead of a regex match anchored to the end of the string. + RUBY + + expect_correction(<<~RUBY) + str.end_with?("\\n") + RUBY end - it "autocorrects /\\n\\z/#{method} str" do - new_source = autocorrect_source("/\\n\\z/#{method} str") - expect(new_source).to eq 'str.end_with?("\n")' + it "registers an offense and corrects /\\n\\z/#{method} str" do + expect_offense(<<~RUBY, method: method) + /\\n\\z/#{method} str + ^^^^^^^{method}^^^^ Use `String#end_with?` instead of a regex match anchored to the end of the string. + RUBY + + expect_correction(<<~RUBY) + str.end_with?("\\n") + RUBY end - it "autocorrects str#{method} /\\t\\z/" do - new_source = autocorrect_source("str#{method} /\\t\\z/") - expect(new_source).to eq 'str.end_with?("\t")' + it "registers an offense and corrects str#{method} /\\t\\z/" do + expect_offense(<<~RUBY, method: method) + str#{method} /\\t\\z/ + ^^^^{method}^^^^^^^ Use `String#end_with?` instead of a regex match anchored to the end of the string. + RUBY + + expect_correction(<<~RUBY) + str.end_with?("\\t") + RUBY end - it "autocorrects /\\t\\z/#{method} str" do - new_source = autocorrect_source("/\\t\\z/#{method} str") - expect(new_source).to eq 'str.end_with?("\t")' + it "registers an offense and corrects /\\t\\z/#{method} str" do + expect_offense(<<~RUBY, method: method) + /\\t\\z/#{method} str + ^^^^^^^{method}^^^^ Use `String#end_with?` instead of a regex match anchored to the end of the string. + RUBY + + expect_correction(<<~RUBY) + str.end_with?("\\t") + RUBY end # regexp metacharacters %w[. $ ^ |].each do |str| - it "autocorrects str#{method} /\\#{str}\\z/" do - new_source = autocorrect_source("str#{method} /\\#{str}\\z/") - expect(new_source).to eq "str.end_with?('#{str}')" + it "registers an offense and corrects str#{method} /\\#{str}\\z/" do + expect_offense(<<~RUBY, method: method, str: str) + str#{method} /\\#{str}\\z/ + ^^^^{method}^^^^{str}^^^ Use `String#end_with?` instead of a regex match anchored to the end of the string. + RUBY + + expect_correction(<<~RUBY) + str.end_with?('#{str}') + RUBY end - it "autocorrects /\\#{str}\\z/#{method} str" do - new_source = autocorrect_source("/\\#{str}\\z/#{method} str") - expect(new_source).to eq "str.end_with?('#{str}')" + it "registers an offense and corrects /\\#{str}\\z/#{method} str" do + expect_offense(<<~RUBY, method: method, str: str) + /\\#{str}\\z/#{method} str + ^^^{str}^^^^{method}^^^^ Use `String#end_with?` instead of a regex match anchored to the end of the string. + RUBY + + expect_correction(<<~RUBY) + str.end_with?('#{str}') + RUBY end it "doesn't register an error for str#{method} /#{str}\\z/" do @@ -71,14 +131,26 @@ # note that "\b" is a literal backspace char in a double-quoted string... # but in a regex, it's an anchor on a word boundary %w[a e f r t v].each do |str| - it "autocorrects str#{method} /\\#{str}\\z/" do - new_source = autocorrect_source("str#{method} /\\#{str}\\z/") - expect(new_source).to eq %{str.end_with?("\\#{str}")} + it "registers an offense and corrects str#{method} /\\#{str}\\z/" do + expect_offense(<<~RUBY, method: method, str: str) + str#{method} /\\#{str}\\z/ + ^^^^{method}^^^^{str}^^^ Use `String#end_with?` instead of a regex match anchored to the end of the string. + RUBY + + expect_correction(<<~RUBY) + str.end_with?("\\#{str}") + RUBY end - it "autocorrects /\\#{str}\\z/#{method} str" do - new_source = autocorrect_source("/\\#{str}\\z/#{method} str") - expect(new_source).to eq %{str.end_with?("\\#{str}")} + it "registers an offense and corrects /\\#{str}\\z/#{method} str" do + expect_offense(<<~RUBY, method: method, str: str) + /\\#{str}\\z/#{method} str + ^^^{str}^^^^{method}^^^^ Use `String#end_with?` instead of a regex match anchored to the end of the string. + RUBY + + expect_correction(<<~RUBY) + str.end_with?("\\#{str}") + RUBY end end @@ -95,39 +167,49 @@ # characters with no special meaning whatsoever %w[i j l m o q y].each do |str| - it "autocorrects str#{method} /\\#{str}\\z/" do - new_source = autocorrect_source("str#{method} /\\#{str}\\z/") - expect(new_source).to eq "str.end_with?('#{str}')" + it "registers an offense and corrects str#{method} /\\#{str}\\z/" do + expect_offense(<<~RUBY, method: method, str: str) + str#{method} /\\#{str}\\z/ + ^^^^{method}^^^^{str}^^^ Use `String#end_with?` instead of a regex match anchored to the end of the string. + RUBY + + expect_correction(<<~RUBY) + str.end_with?('#{str}') + RUBY end - it "autocorrects /\\#{str}\\z/#{method} str" do - new_source = autocorrect_source("/\\#{str}\\z/#{method} str") - expect(new_source).to eq "str.end_with?('#{str}')" + it "registers an offense and corrects /\\#{str}\\z/#{method} str" do + expect_offense(<<~RUBY, method: method, str: str) + /\\#{str}\\z/#{method} str + ^^^{str}^^^^{method}^^^^ Use `String#end_with?` instead of a regex match anchored to the end of the string. + RUBY + + expect_correction(<<~RUBY) + str.end_with?('#{str}') + RUBY end end - it "formats the error message correctly for str#{method} /abc\\z/" do - inspect_source("str#{method} /abc\\z/") - expect(cop.messages).to eq(['Use `String#end_with?` instead of a ' \ - 'regex match anchored to the end of ' \ - 'the string.']) - end + it "registers an offense and corrects str#{method} /\\\\\\z/" do + expect_offense(<<~RUBY, method: method) + str#{method} /\\\\\\z/ + ^^^^{method}^^^^^^^ Use `String#end_with?` instead of a regex match anchored to the end of the string. + RUBY - it "formats the error message correctly for /abc\\z/#{method} str" do - inspect_source("/abc\\z/#{method} str") - expect(cop.messages).to eq(['Use `String#end_with?` instead of a ' \ - 'regex match anchored to the end of ' \ - 'the string.']) + expect_correction(<<~RUBY) + str.end_with?('\\\\') + RUBY end - it "autocorrects str#{method} /\\\\\\z/" do - new_source = autocorrect_source("str#{method} /\\\\\\z/") - expect(new_source).to eq("str.end_with?('\\\\')") - end + it "registers an offense and corrects /\\\\\\z/#{method} str" do + expect_offense(<<~RUBY, method: method) + /\\\\\\z/#{method} str + ^^^^^^^{method}^^^^ Use `String#end_with?` instead of a regex match anchored to the end of the string. + RUBY - it "autocorrects /\\\\\\z/#{method} str" do - new_source = autocorrect_source("/\\\\\\z/#{method} str") - expect(new_source).to eq("str.end_with?('\\\\')") + expect_correction(<<~RUBY) + str.end_with?('\\\\') + RUBY end end diff --git a/spec/rubocop/cop/performance/fixed_size_spec.rb b/spec/rubocop/cop/performance/fixed_size_spec.rb index 4f2e92a..639433a 100644 --- a/spec/rubocop/cop/performance/fixed_size_spec.rb +++ b/spec/rubocop/cop/performance/fixed_size_spec.rb @@ -9,65 +9,62 @@ shared_examples 'common functionality' do |method| context 'strings' do - it "registers an offense when calling #{method} on a single quoted " \ - 'string' do - inspect_source("'a'.#{method}") - - expect(cop.messages).to eq([message]) + it "registers an offense when calling #{method} on a single quoted string" do + expect_offense(<<~RUBY, method: method) + 'a'.#{method} + ^^^^^{method} Do not compute the size of statically sized objects. + RUBY end - it "registers an offense when calling #{method} on a double quoted " \ - 'string' do - inspect_source("\"a\".#{method}") - - expect(cop.messages).to eq([message]) + it "registers an offense when calling #{method} on a double quoted string" do + expect_offense(<<~RUBY, method: method) + "a".#{method} + ^^^^^{method} Do not compute the size of statically sized objects. + RUBY end it "registers an offense when calling #{method} on a %q string" do - inspect_source("%q(a).#{method}") - - expect(cop.messages).to eq([message]) + expect_offense(<<~RUBY, method: method) + %q(a).#{method} + ^^^^^^^{method} Do not compute the size of statically sized objects. + RUBY end it "registers an offense when calling #{method} on a %Q string" do - inspect_source("%Q(a).#{method}") - - expect(cop.messages).to eq([message]) + expect_offense(<<~RUBY, method: method) + %Q(a).#{method} + ^^^^^^^{method} Do not compute the size of statically sized objects. + RUBY end it "registers an offense when calling #{method} on a % string" do - inspect_source("%(a).#{method}") - - expect(cop.messages).to eq([message]) + expect_offense(<<~RUBY, method: method) + %(a).#{method} + ^^^^^^{method} Do not compute the size of statically sized objects. + RUBY end - it "accepts calling #{method} on a double quoted string that " \ - 'contains interpolation' do + it "accepts calling #{method} on a double quoted string that contains interpolation" do expect_no_offenses("\"\#{foo}\".#{method}") end - it "accepts calling #{method} on a %Q string that contains " \ - 'interpolation' do + it "accepts calling #{method} on a %Q string that contains interpolation" do expect_no_offenses("\%Q(\#{foo}).#{method}") end - it "accepts calling #{method} on a % string that contains " \ - 'interpolation' do + it "accepts calling #{method} on a % string that contains interpolation" do expect_no_offenses("\%(\#{foo}).#{method}") end - it "accepts calling #{method} on a single quoted string that " \ - 'is assigned to a constant' do + it "accepts calling #{method} on a single quoted string that is assigned to a constant" do expect_no_offenses("CONST = 'a'.#{method}") end - it "accepts calling #{method} on a double quoted string that " \ - 'is assigned to a constant' do + it "accepts calling #{method} on a double quoted string that is assigned to a constant" do expect_no_offenses("CONST = \"a\".#{method}") end - it "accepts calling #{method} on a %q string that is assigned to " \ - 'a constant' do + it "accepts calling #{method} on a %q string that is assigned to a constant" do expect_no_offenses("CONST = %q(a).#{method}") end @@ -86,15 +83,17 @@ context 'symbols' do it "registers an offense when calling #{method} on a symbol" do - inspect_source(":foo.#{method}") - - expect(cop.messages).to eq([message]) + expect_offense(<<~RUBY, method: method) + :foo.#{method} + ^^^^^^{method} Do not compute the size of statically sized objects. + RUBY end it "registers an offense when calling #{method} on a quoted symbol" do - inspect_source(":'foo-bar'.#{method}") - - expect(cop.messages).to eq([message]) + expect_offense(<<~RUBY, method: method) + :'foo-bar'.#{method} + ^^^^^^^^^^^^{method} Do not compute the size of statically sized objects. + RUBY end it "accepts calling #{method} on an interpolated quoted symbol" do @@ -102,38 +101,40 @@ end it "registers an offense when calling #{method} on %s" do - inspect_source("%s(foo-bar).#{method}") - - expect(cop.messages).to eq([message]) + expect_offense(<<~RUBY, method: method) + %s(foo-bar).#{method} + ^^^^^^^^^^^^^{method} Do not compute the size of statically sized objects. + RUBY end - it "accepts calling #{method} on a symbol that is assigned " \ - 'to a constant' do + it "accepts calling #{method} on a symbol that is assigned to a constant" do expect_no_offenses("CONST = :foo.#{method}") end end context 'arrays' do it "registers an offense when calling #{method} on an array using []" do - inspect_source("[1, 2, foo].#{method}") - - expect(cop.messages).to eq([message]) + expect_offense(<<~RUBY, method: method) + [1, 2, foo].#{method} + ^^^^^^^^^^^^^{method} Do not compute the size of statically sized objects. + RUBY end it "registers an offense when calling #{method} on an array using %w" do - inspect_source("%w(1, 2, foo).#{method}") - - expect(cop.messages).to eq([message]) + expect_offense(<<~RUBY, method: method) + %w(1, 2, foo).#{method} + ^^^^^^^^^^^^^^^{method} Do not compute the size of statically sized objects. + RUBY end it "registers an offense when calling #{method} on an array using %W" do - inspect_source("%W(1, 2, foo).#{method}") - - expect(cop.messages).to eq([message]) + expect_offense(<<~RUBY, method: method) + %W(1, 2, foo).#{method} + ^^^^^^^^^^^^^^^{method} Do not compute the size of statically sized objects. + RUBY end - it "accepts calling #{method} on an array using [] that contains " \ - 'a splat' do + it "accepts calling #{method} on an array using [] that contains a splat" do expect_no_offenses("[1, 2, *foo].#{method}") end @@ -144,17 +145,17 @@ RUBY end - it "accepts calling #{method} on an array that is assigned " \ - 'to a constant' do + it "accepts calling #{method} on an array that is assigned to a constant" do expect_no_offenses("CONST = [1, 2, 3].#{method}") end end context 'hashes' do it "registers an offense when calling #{method} on a hash using {}" do - inspect_source("{a: 1, b: 2}.#{method}") - - expect(cop.messages).to eq([message]) + expect_offense(<<~RUBY, method: method) + {a: 1, b: 2}.#{method} + ^^^^^^^^^^^^^^{method} Do not compute the size of statically sized objects. + RUBY end it "accepts calling #{method} on a hash set to a variable" do @@ -189,9 +190,10 @@ end it 'registers an offense when calling count with a string' do - inspect_source("#{variable}.count('o')") - - expect(cop.messages).to eq([message]) + expect_offense(<<~RUBY, variable: variable) + #{variable}.count('o') + ^{variable}^^^^^^^^^^^ Do not compute the size of statically sized objects. + RUBY end it 'accepts calling count with a block' do diff --git a/spec/rubocop/cop/performance/flat_map_spec.rb b/spec/rubocop/cop/performance/flat_map_spec.rb index e9242be..c02e924 100644 --- a/spec/rubocop/cop/performance/flat_map_spec.rb +++ b/spec/rubocop/cop/performance/flat_map_spec.rb @@ -4,28 +4,37 @@ subject(:cop) { described_class.new(config) } shared_examples 'map_and_collect' do |method, flatten| - it "registers an offense when calling #{method}...#{flatten}(1)" do - inspect_source("[1, 2, 3, 4].#{method} { |e| [e, e] }.#{flatten}(1)") - - expect(cop.messages) - .to eq(["Use `flat_map` instead of `#{method}...#{flatten}`."]) - expect(cop.highlights).to eq(["#{method} { |e| [e, e] }.#{flatten}(1)"]) + it "registers an offense and corrects when calling #{method}...#{flatten}(1)" do + expect_offense(<<~RUBY, method: method, flatten: flatten) + [1, 2, 3, 4].#{method} { |e| [e, e] }.#{flatten}(1) + ^{method}^^^^^^^^^^^^^^^^^{flatten}^^^ Use `flat_map` instead of `#{method}...#{flatten}`. + RUBY + + expect_correction(<<~RUBY) + [1, 2, 3, 4].flat_map { |e| [e, e] } + RUBY end - it "registers an offense when calling #{method}(&:foo).#{flatten}(1)" do - inspect_source("[1, 2, 3, 4].#{method}(&:foo).#{flatten}(1)") + it "registers an offense and corrects when calling #{method}(&:foo).#{flatten}(1)" do + expect_offense(<<~RUBY, method: method, flatten: flatten) + [1, 2, 3, 4].#{method}(&:foo).#{flatten}(1) + ^{method}^^^^^^^^^{flatten}^^^ Use `flat_map` instead of `#{method}...#{flatten}`. + RUBY - expect(cop.messages) - .to eq(["Use `flat_map` instead of `#{method}...#{flatten}`."]) - expect(cop.highlights).to eq(["#{method}(&:foo).#{flatten}(1)"]) + expect_correction(<<~RUBY) + [1, 2, 3, 4].flat_map(&:foo) + RUBY end - it "registers an offense when calling #{method}(&foo).#{flatten}(1)" do - inspect_source("[1, 2, 3, 4].#{method}(&foo).#{flatten}(1)") + it "registers an offense and corrects when calling #{method}(&foo).#{flatten}(1)" do + expect_offense(<<~RUBY, method: method, flatten: flatten) + [1, 2, 3, 4].#{method}(&foo).#{flatten}(1) + ^{method}^^^^^^^^{flatten}^^^ Use `flat_map` instead of `#{method}...#{flatten}`. + RUBY - expect(cop.messages) - .to eq(["Use `flat_map` instead of `#{method}...#{flatten}`."]) - expect(cop.highlights).to eq(["#{method}(&foo).#{flatten}(1)"]) + expect_correction(<<~RUBY) + [1, 2, 3, 4].flat_map(&foo) + RUBY end it "does not register an offense when calling #{method}...#{flatten} " \ @@ -36,27 +45,6 @@ it "does not register an offense when calling #{method}!...#{flatten}" do expect_no_offenses("[1, 2, 3, 4].#{method}! { |e| [e, e] }.#{flatten}") end - - it "corrects #{method}..#{flatten}(1) to flat_map" do - source = "[1, 2].#{method} { |e| [e, e] }.#{flatten}(1)" - new_source = autocorrect_source(source) - - expect(new_source).to eq('[1, 2].flat_map { |e| [e, e] }') - end - - it "corrects #{method}(&:foo).#{flatten} to flat_map" do - source = "[1, 2].#{method}(&:foo).#{flatten}(1)" - new_source = autocorrect_source(source) - - expect(new_source).to eq('[1, 2].flat_map(&:foo)') - end - - it "corrects #{method}(&foo).#{flatten} to flat_map" do - source = "[1, 2].#{method}(&:foo).#{flatten}(1)" - new_source = autocorrect_source(source) - - expect(new_source).to eq('[1, 2].flat_map(&:foo)') - end end describe 'configured to only warn when flattening one level' do @@ -94,20 +82,12 @@ shared_examples 'flatten_with_params_enabled' do |method, flatten| it "registers an offense when calling #{method}...#{flatten}" do - inspect_source("[1, 2, 3, 4].map { |e| [e, e] }.#{flatten}") - - expect(cop.messages) - .to eq(["Use `flat_map` instead of `map...#{flatten}`. " \ - 'Beware, `flat_map` only flattens 1 level and `flatten` ' \ - 'can be used to flatten multiple levels.']) - expect(cop.highlights).to eq(["map { |e| [e, e] }.#{flatten}"]) - end - - it "will not correct #{method}..#{flatten} to flat_map" do - source = "[1, 2].map { |e| [e, e] }.#{flatten}" - new_source = autocorrect_source(source) + expect_offense(<<~RUBY, method: method, flatten: flatten) + [1, 2, 3, 4].#{method} { |e| [e, e] }.#{flatten} + ^{method}^^^^^^^^^^^^^^^^^{flatten} Use `flat_map` instead of `#{method}...#{flatten}`. Beware, `flat_map` only flattens 1 level and `flatten` can be used to flatten multiple levels. + RUBY - expect(new_source).to eq("[1, 2].map { |e| [e, e] }.#{flatten}") + expect_no_corrections end end diff --git a/spec/rubocop/cop/performance/regexp_match_spec.rb b/spec/rubocop/cop/performance/regexp_match_spec.rb index 1eae5fe..5623413 100644 --- a/spec/rubocop/cop/performance/regexp_match_spec.rb +++ b/spec/rubocop/cop/performance/regexp_match_spec.rb @@ -5,9 +5,9 @@ shared_examples 'offense' do |name, code, correction| it "registers an offense for #{name}" do - inspect_source(code) + offenses = inspect_source(code) - expect(cop.offenses.size).to eq(1) + expect(offenses.size).to eq(1) end it "corrects #{name}" do diff --git a/spec/rubocop/cop/performance/start_with_spec.rb b/spec/rubocop/cop/performance/start_with_spec.rb index 660afa3..8dc43f4 100644 --- a/spec/rubocop/cop/performance/start_with_spec.rb +++ b/spec/rubocop/cop/performance/start_with_spec.rb @@ -6,108 +6,166 @@ let(:cop_config) { { 'SafeMultiline' => safe_multiline } } shared_examples 'different match methods' do |method| - it "autocorrects str#{method} /\\Aabc/" do - new_source = autocorrect_source("str#{method} /\\Aabc/") - expect(new_source).to eq "str.start_with?('abc')" + it "registers an offense and corrects str#{method} /\\Aabc/" do + expect_offense(<<~RUBY, method: method) + str#{method} /\\Aabc/ + ^^^^{method}^^^^^^^^ Use `String#start_with?` instead of a regex match anchored to the beginning of the string. + RUBY + + expect_correction(<<~RUBY) + str.start_with?('abc') + RUBY end - it "autocorrects /\\Aabc/#{method} str" do - new_source = autocorrect_source("/\\Aabc/#{method} str") - expect(new_source).to eq "str.start_with?('abc')" + it "registers an offense and corrects /\\Aabc/#{method} str" do + expect_offense(<<~RUBY, method: method) + /\\Aabc/#{method} str + ^^^^^^^^^^{method}^^ Use `String#start_with?` instead of a regex match anchored to the beginning of the string. + RUBY + + expect_correction(<<~RUBY) + str.start_with?('abc') + RUBY end - it "autocorrects str#{method} /^abc/" do - new_source = autocorrect_source("str#{method} /^abc/") - expect(new_source).to eq "str.start_with?('abc')" + it "registers an offense and corrects str#{method} /^abc/" do + expect_offense(<<~RUBY, method: method) + str#{method} /^abc/ + ^^^^{method}^^^^^^^ Use `String#start_with?` instead of a regex match anchored to the beginning of the string. + RUBY + + expect_correction(<<~RUBY) + str.start_with?('abc') + RUBY end - it "autocorrects /^abc/#{method} str" do - new_source = autocorrect_source("/^abc/#{method} str") - expect(new_source).to eq "str.start_with?('abc')" + it "registers an offense and corrects /^abc/#{method} str" do + expect_offense(<<~RUBY, method: method) + /^abc/#{method} str + ^^^^{method}^^^^^^^ Use `String#start_with?` instead of a regex match anchored to the beginning of the string. + RUBY + + expect_correction(<<~RUBY) + str.start_with?('abc') + RUBY end # escapes like "\n" # note that "\b" is a literal backspace char in a double-quoted string... # but in a regex, it's an anchor on a word boundary %w[a e f r t v].each do |str| - it "autocorrects str#{method} /\\A\\#{str}/" do - new_source = autocorrect_source("str#{method} /\\A\\#{str}/") - expect(new_source).to eq %{str.start_with?("\\#{str}")} + it "registers an offense and corrects str#{method} /\\A\\#{str}/" do + expect_offense(<<~RUBY, method: method, str: str) + str#{method} /\\A\\#{str}/ + ^^^^{method}^^^^^^{str}^ Use `String#start_with?` instead of a regex match anchored to the beginning of the string. + RUBY + + expect_correction(<<~RUBY) + str.start_with?("\\#{str}") + RUBY end - it "autocorrects /\\A\\#{str}#{method} str/" do - new_source = autocorrect_source("/\\A\\#{str}/#{method} str") - expect(new_source).to eq %{str.start_with?("\\#{str}")} + it "registers an offense and corrects /\\A\\#{str}#{method} str/" do + expect_offense(<<~RUBY, method: method, str: str) + /\\A\\#{str}/#{method} str + ^^^^^{str}^^{method}^^^^ Use `String#start_with?` instead of a regex match anchored to the beginning of the string. + RUBY + + expect_correction(<<~RUBY) + str.start_with?("\\#{str}") + RUBY end end # regexp metacharacters %w[. * ? $ ^ |].each do |str| - it "autocorrects str#{method} /\\A\\#{str}/" do - new_source = autocorrect_source("str#{method} /\\A\\#{str}/") - expect(new_source).to eq "str.start_with?('#{str}')" + it "registers an offense and corrects str#{method} /\\A\\#{str}/" do + expect_offense(<<~RUBY, method: method, str: str) + str#{method} /\\A\\#{str}/ + ^^^^{method}^^^^^^{str}^ Use `String#start_with?` instead of a regex match anchored to the beginning of the string. + RUBY + + expect_correction(<<~RUBY) + str.start_with?('#{str}') + RUBY end - it "autocorrects /\\A\\#{str}/#{method} str" do - new_source = autocorrect_source("/\\A\\#{str}/#{method} str") - expect(new_source).to eq "str.start_with?('#{str}')" + it "registers an offense and corrects /\\A\\#{str}/#{method} str" do + expect_offense(<<~RUBY, method: method, str: str) + /\\A\\#{str}/#{method} str + ^^^^^{str}^^{method}^^^^ Use `String#start_with?` instead of a regex match anchored to the beginning of the string. + RUBY + + expect_correction(<<~RUBY) + str.start_with?('#{str}') + RUBY end - it "doesn't register an error for str#{method} /\\A#{str}/" do + it "doesn't register an offense for str#{method} /\\A#{str}/" do expect_no_offenses("str#{method} /\\A#{str}/") end - it "doesn't register an error for /\\A#{str}/#{method} str" do + it "doesn't register an offense for /\\A#{str}/#{method} str" do expect_no_offenses("/\\A#{str}/#{method} str") end end # character classes, anchors %w[w W s S d D A Z z G b B h H R X S].each do |str| - it "doesn't register an error for str#{method} /\\A\\#{str}/" do + it "doesn't register an offense for str#{method} /\\A\\#{str}/" do expect_no_offenses("str#{method} /\\A\\#{str}/") end - it "doesn't register an error for /\\A\\#{str}/#{method} str" do + it "doesn't register an offense for /\\A\\#{str}/#{method} str" do expect_no_offenses("/\\A\\#{str}/#{method} str") end end # characters with no special meaning whatsoever %w[i j l m o q y].each do |str| - it "autocorrects str#{method} /\\A\\#{str}/" do - new_source = autocorrect_source("str#{method} /\\A\\#{str}/") - expect(new_source).to eq "str.start_with?('#{str}')" + it "registers an offense and corrects str#{method} /\\A\\#{str}/" do + expect_offense(<<~RUBY, method: method, str: str) + str#{method} /\\A\\#{str}/ + ^^^^{method}^^^^^^{str}^ Use `String#start_with?` instead of a regex match anchored to the beginning of the string. + RUBY + + expect_correction(<<~RUBY) + str.start_with?('#{str}') + RUBY end - it "autocorrects /\\A\\#{str}#{method} str/" do - new_source = autocorrect_source("/\\A\\#{str}/#{method} str") - expect(new_source).to eq "str.start_with?('#{str}')" + it "registers an offense and corrects /\\A\\#{str}#{method} str/" do + expect_offense(<<~RUBY, method: method, str: str) + /\\A\\#{str}/#{method} str + ^^^^^{str}^^{method}^^^^ Use `String#start_with?` instead of a regex match anchored to the beginning of the string. + RUBY + + expect_correction(<<~RUBY) + str.start_with?('#{str}') + RUBY end end - it "formats the error message correctly for str#{method} /\\Aabc/" do - inspect_source("str#{method} /\\Aabc/") - expect(cop.messages).to eq(['Use `String#start_with?` instead of a ' \ - 'regex match anchored to the beginning of ' \ - 'the string.']) - end + it "registers an offense and corrects str#{method} /\\A\\\\/" do + expect_offense(<<~RUBY, method: method) + str#{method} /\\A\\\\/ + ^^^^{method}^^^^^^^ Use `String#start_with?` instead of a regex match anchored to the beginning of the string. + RUBY - it "formats the error message correctly for /\\Aabc/#{method} str" do - inspect_source("/\\Aabc/#{method} str") - expect(cop.messages).to eq(['Use `String#start_with?` instead of a ' \ - 'regex match anchored to the beginning of ' \ - 'the string.']) + expect_correction(<<~RUBY) + str.start_with?('\\\\') + RUBY end - it "autocorrects str#{method} /\\A\\\\/" do - new_source = autocorrect_source("str#{method} /\\A\\\\/") - expect(new_source).to eq("str.start_with?('\\\\')") - end + it "registers an offense and corrects /\\A\\\\/#{method} str" do + expect_offense(<<~RUBY, method: method) + /\\A\\\\/#{method} str + ^^^^^^^{method}^^^^ Use `String#start_with?` instead of a regex match anchored to the beginning of the string. + RUBY - it "autocorrects /\\A\\\\/#{method} str" do - new_source = autocorrect_source("/\\A\\\\/#{method} str") - expect(new_source).to eq("str.start_with?('\\\\')") + expect_correction(<<~RUBY) + str.start_with?('\\\\') + RUBY end end diff --git a/spec/rubocop/cop/performance/string_include_spec.rb b/spec/rubocop/cop/performance/string_include_spec.rb index d388b7b..11e5403 100644 --- a/spec/rubocop/cop/performance/string_include_spec.rb +++ b/spec/rubocop/cop/performance/string_include_spec.rb @@ -4,41 +4,77 @@ subject(:cop) { described_class.new } shared_examples 'different match methods' do |method| - it "autocorrects str#{method} /abc/" do - new_source = autocorrect_source("str#{method} /abc/") - expect(new_source).to eq "str.include?('abc')" + it "registers an offense and corrects str#{method} /abc/" do + expect_offense(<<~RUBY, method: method) + str#{method} /abc/ + ^^^^{method}^^^^^^ Use `String#include?` instead of a regex match with literal-only pattern. + RUBY + + expect_correction(<<~RUBY) + str.include?('abc') + RUBY end - it "autocorrects /abc/#{method} str" do - new_source = autocorrect_source("/abc/#{method} 'str'") - expect(new_source).to eq "'str'.include?('abc')" + it "registers an offense and corrects /abc/#{method} str" do + expect_offense(<<~RUBY, method: method) + /abc/#{method} 'str' + ^^^^^^{method}^^^^^^ Use `String#include?` instead of a regex match with literal-only pattern. + RUBY + + expect_correction(<<~RUBY) + 'str'.include?('abc') + RUBY end # escapes like "\n" # note that "\b" is a literal backspace char in a double-quoted string... # but in a regex, it's an anchor on a word boundary %w[a e f r t v].each do |str| - it "autocorrects str#{method} /\\#{str}/" do - new_source = autocorrect_source("str#{method} /\\#{str}/") - expect(new_source).to eq %{str.include?("\\#{str}")} + it "registers an offense and corrects str#{method} /\\#{str}/" do + expect_offense(<<~RUBY, method: method, str: str) + str#{method} /\\#{str}/ + ^^^^{method}^^^^{str}^ Use `String#include?` instead of a regex match with literal-only pattern. + RUBY + + expect_correction(<<~RUBY) + str.include?("\\#{str}") + RUBY end - it "autocorrects /\\#{str}#{method} str/" do - new_source = autocorrect_source("/\\#{str}/#{method} 'str'") - expect(new_source).to eq %{'str'.include?("\\#{str}")} + it "registers an offense and corrects /\\#{str}#{method} str/" do + expect_offense(<<~RUBY, method: method, str: str) + /\\#{str}/#{method} 'str' + ^^^{str}^^{method}^^^^^^ Use `String#include?` instead of a regex match with literal-only pattern. + RUBY + + expect_correction(<<~RUBY) + 'str'.include?("\\#{str}") + RUBY end end # regexp metacharacters %w[. * ? $ ^ |].each do |str| - it "autocorrects str#{method} /\\#{str}/" do - new_source = autocorrect_source("str#{method} /\\#{str}/") - expect(new_source).to eq "str.include?('#{str}')" + it "registers an offense and corrects str#{method} /\\#{str}/" do + expect_offense(<<~RUBY, method: method, str: str) + str#{method} /\\#{str}/ + ^^^^{method}^^^^{str}^ Use `String#include?` instead of a regex match with literal-only pattern. + RUBY + + expect_correction(<<~RUBY) + str.include?('#{str}') + RUBY end - it "autocorrects /\\#{str}/#{method} str" do - new_source = autocorrect_source("/\\#{str}/#{method} 'str'") - expect(new_source).to eq "'str'.include?('#{str}')" + it "registers an offense and corrects /\\#{str}/#{method} str" do + expect_offense(<<~RUBY, method: method, str: str) + /\\#{str}/#{method} 'str' + ^^^{str}^^{method}^^^^^^ Use `String#include?` instead of a regex match with literal-only pattern. + RUBY + + expect_correction(<<~RUBY) + 'str'.include?('#{str}') + RUBY end it "doesn't register an error for str#{method} /prefix#{str}/" do @@ -63,35 +99,49 @@ # characters with no special meaning whatsoever %w[i j l m o q y].each do |str| - it "autocorrects str#{method} /\\#{str}/" do - new_source = autocorrect_source("str#{method} /\\#{str}/") - expect(new_source).to eq "str.include?('#{str}')" + it "registers an offense and corrects str#{method} /\\#{str}/" do + expect_offense(<<~RUBY, method: method, str: str) + str#{method} /\\#{str}/ + ^^^^{method}^^^^{str}^ Use `String#include?` instead of a regex match with literal-only pattern. + RUBY + + expect_correction(<<~RUBY) + str.include?('#{str}') + RUBY end - it "autocorrects /\\#{str}#{method} str/" do - new_source = autocorrect_source("/\\#{str}/#{method} 'str'") - expect(new_source).to eq "'str'.include?('#{str}')" + it "registers an offense and corrects /\\#{str}#{method} str/" do + expect_offense(<<~RUBY, method: method, str: str) + /\\#{str}/#{method} 'str' + ^^^{str}^{method}^^^^^^^ Use `String#include?` instead of a regex match with literal-only pattern. + RUBY + + expect_correction(<<~RUBY) + 'str'.include?('#{str}') + RUBY end end - it "formats the error message correctly for str#{method} /abc/" do - inspect_source("str#{method} /abc/") - expect(cop.messages).to eq(['Use `String#include?` instead of a regex match with literal-only pattern.']) - end + it "registers an offense and corrects str#{method} /\\\\/" do + expect_offense(<<~RUBY, method: method) + str#{method} /\\\\/ + ^^^^{method}^^^^^ Use `String#include?` instead of a regex match with literal-only pattern. + RUBY - it "formats the error message correctly for /abc/#{method} str" do - inspect_source("/abc/#{method} 'str'") - expect(cop.messages).to eq(['Use `String#include?` instead of a regex match with literal-only pattern.']) + expect_correction(<<~RUBY) + str.include?('\\\\') + RUBY end - it "autocorrects str#{method} /\\\\/" do - new_source = autocorrect_source("str#{method} /\\\\/") - expect(new_source).to eq("str.include?('\\\\')") - end + it "registers an offense and corrects /\\\\/#{method} str" do + expect_offense(<<~RUBY, method: method) + /\\\\/#{method} 'str' + ^^^^^{method}^^^^^^ Use `String#include?` instead of a regex match with literal-only pattern. + RUBY - it "autocorrects /\\\\/#{method} str" do - new_source = autocorrect_source("/\\\\/#{method} 'str'") - expect(new_source).to eq("'str'.include?('\\\\')") + expect_correction(<<~RUBY) + 'str'.include?('\\\\') + RUBY end end diff --git a/spec/rubocop/cop/performance/string_replacement_spec.rb b/spec/rubocop/cop/performance/string_replacement_spec.rb index ae36f5d..957cfc0 100644 --- a/spec/rubocop/cop/performance/string_replacement_spec.rb +++ b/spec/rubocop/cop/performance/string_replacement_spec.rb @@ -108,13 +108,17 @@ %w[a b c ' " % ! = < > # & ; : ` ~ 1 2 3 - _ , \r \\\\ \y \u1234 \x65].each do |str| it "registers an offense when replacing #{str} with a literal" do - inspect_source("'abc'.gsub(/#{str}/, 'a')") - expect(cop.messages).to eq(['Use `tr` instead of `gsub`.']) + expect_offense(<<~RUBY, str: str) + 'abc'.gsub(/#{str}/, 'a') + ^^^^^^^{str}^^^^^^^ Use `tr` instead of `gsub`. + RUBY end it "registers an offense when deleting #{str}" do - inspect_source("'abc'.gsub(/#{str}/, '')") - expect(cop.messages).to eq(['Use `delete` instead of `gsub`.']) + expect_offense(<<~RUBY, str: str) + 'abc'.gsub(/#{str}/, '') + ^^^^^^^{str}^^^^^^ Use `delete` instead of `gsub`. + RUBY end end @@ -124,9 +128,10 @@ end it 'registers an offense when escape characters in regex' do - inspect_source(%('abc'.gsub(/\n/, ','))) - - expect(cop.messages).to eq(['Use `tr` instead of `gsub`.']) + expect_offense(<<~RUBY) + 'abc'.gsub(/\\n/, ',') + ^^^^^^^^^^^^^^^ Use `tr` instead of `gsub`. + RUBY end it 'registers an offense when using %r notation' do @@ -263,18 +268,6 @@ RUBY end - it 'registers an offense when using escape characters in the replacement' do - inspect_source("'abc'.gsub('a', '\n')") - - expect(cop.messages).to eq(['Use `tr` instead of `gsub`.']) - end - - it 'registers an offense when using escape characters in the pattern' do - inspect_source("'abc'.gsub('\n', ',')") - - expect(cop.messages).to eq(['Use `tr` instead of `gsub`.']) - end - context 'auto-correct' do describe 'corrects to tr' do it 'corrects when the length of the pattern and replacement are one' do diff --git a/spec/rubocop/cop/performance/sum_spec.rb b/spec/rubocop/cop/performance/sum_spec.rb index 5a5d927..b0b10f7 100644 --- a/spec/rubocop/cop/performance/sum_spec.rb +++ b/spec/rubocop/cop/performance/sum_spec.rb @@ -5,53 +5,51 @@ %i[inject reduce].each do |method| it "registers an offense and corrects when using `array.#{method}(10, :+)`" do - source = "array.#{method}(10, :+)" - inspect_source(source) - - expect(cop.offenses.size).to eq(1) - expect(cop.highlights).to eq(["#{method}(10, :+)"]) + expect_offense(<<~RUBY, method: method) + array.#{method}(10, :+) + ^{method}^^^^^^^^ Use `sum(10)` instead of `#{method}(10, :+)`. + RUBY - new_source = autocorrect_source(source) - expect(new_source).to eq('array.sum(10)') + expect_correction(<<~RUBY) + array.sum(10) + RUBY end it "registers an offense and corrects when using `array.#{method}(10) { |acc, elem| acc + elem }`" do - source = "array.#{method}(10) { |acc, elem| acc + elem }" - inspect_source(source) - - expect(cop.offenses.size).to eq(1) - expect(cop.highlights).to eq(["#{method}(10) { |acc, elem| acc + elem }"]) + expect_offense(<<~RUBY, method: method) + array.#{method}(10) { |acc, elem| acc + elem } + ^{method}^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Use `sum(10)` instead of `#{method}(10) { |acc, elem| acc + elem }`. + RUBY - new_source = autocorrect_source(source) - expect(new_source).to eq('array.sum(10)') + expect_correction(<<~RUBY) + array.sum(10) + RUBY end it "registers an offense and corrects when using `array.#{method}(10) { |acc, elem| elem + acc }`" do - source = "array.#{method}(10) { |acc, elem| elem + acc }" - inspect_source(source) - - expect(cop.offenses.size).to eq(1) - expect(cop.highlights).to eq(["#{method}(10) { |acc, elem| elem + acc }"]) + expect_offense(<<~RUBY, method: method) + array.#{method}(10) { |acc, elem| elem + acc } + ^{method}^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Use `sum(10)` instead of `#{method}(10) { |acc, elem| elem + acc }`. + RUBY - new_source = autocorrect_source(source) - expect(new_source).to eq('array.sum(10)') + expect_correction(<<~RUBY) + array.sum(10) + RUBY end it 'does not autocorrect when initial value is not provided' do - source = "array.#{method}(:+)" - inspect_source(source) - - expect(cop.offenses.size).to eq(1) - expect(cop.highlights).to eq(["#{method}(:+)"]) + expect_offense(<<~RUBY, method: method) + array.#{method}(:+) + ^{method}^^^^ Use `sum` instead of `#{method}(:+)`. + RUBY - new_source = autocorrect_source(source) - expect(new_source).to eq(source) + expect_no_corrections end it 'does not register an offense when block does not implement summation' do - source = "array.#{method} { |acc, elem| elem * 2 }" - inspect_source(source) - expect(cop.offenses.size).to eq(0) + expect_no_offenses(<<~RUBY) + array.#{method} { |acc, elem| elem * 2 } + RUBY end it 'does not register an offense when using `sum`' do diff --git a/spec/rubocop/cop/performance/times_map_spec.rb b/spec/rubocop/cop/performance/times_map_spec.rb index bcf439e..cbc811a 100644 --- a/spec/rubocop/cop/performance/times_map_spec.rb +++ b/spec/rubocop/cop/performance/times_map_spec.rb @@ -3,72 +3,60 @@ RSpec.describe RuboCop::Cop::Performance::TimesMap do subject(:cop) { described_class.new } - before do - inspect_source(source) - end - shared_examples 'map_or_collect' do |method| context ".times.#{method}" do context 'with a block' do - let(:source) { "4.times.#{method} { |i| i.to_s }" } - - it 'registers an offense' do - expect(cop.offenses.size).to eq(1) - expect(cop.offenses.first.message).to eq( - "Use `Array.new(4)` with a block instead of `.times.#{method}`." - ) - expect(cop.highlights).to eq(["4.times.#{method} { |i| i.to_s }"]) - end + it 'registers an offense and corrects' do + expect_offense(<<~RUBY, method: method) + 4.times.#{method} { |i| i.to_s } + ^^^^^^^^^{method}^^^^^^^^^^^^^^^ Use `Array.new(4)` with a block instead of `.times.#{method}`. + RUBY - it 'auto-corrects' do - corrected = autocorrect_source(source) - expect(corrected).to eq('Array.new(4) { |i| i.to_s }') + expect_correction(<<~RUBY) + Array.new(4) { |i| i.to_s } + RUBY end end context 'for non-literal receiver' do - let(:source) { "n.times.#{method} { |i| i.to_s }" } - it 'registers an offense' do - expect(cop.offenses.size).to eq(1) - expect(cop.offenses.first.message).to eq( - "Use `Array.new(n)` with a block instead of `.times.#{method}` " \ - 'only if `n` is always 0 or more.' - ) - expect(cop.highlights).to eq(["n.times.#{method} { |i| i.to_s }"]) + expect_offense(<<~RUBY, method: method) + n.times.#{method} { |i| i.to_s } + ^^^^^^^^^{method}^^^^^^^^^^^^^^^ Use `Array.new(n)` with a block instead of `.times.#{method}` only if `n` is always 0 or more. + RUBY + + expect_correction(<<~RUBY) + Array.new(n) { |i| i.to_s } + RUBY end end context 'with an explicitly passed block' do - let(:source) { "4.times.#{method}(&method(:foo))" } - - it 'registers an offense' do - expect(cop.offenses.size).to eq(1) - expect(cop.offenses.first.message).to eq( - "Use `Array.new(4)` with a block instead of `.times.#{method}`." - ) - expect(cop.highlights).to eq(["4.times.#{method}(&method(:foo))"]) - end + it 'registers an offense and corrects' do + expect_offense(<<~RUBY, method: method) + 4.times.#{method}(&method(:foo)) + ^^^^^^^^^{method}^^^^^^^^^^^^^^^ Use `Array.new(4)` with a block instead of `.times.#{method}`. + RUBY - it 'auto-corrects' do - corrected = autocorrect_source(source) - expect(corrected).to eq('Array.new(4, &method(:foo))') + expect_correction(<<~RUBY) + Array.new(4, &method(:foo)) + RUBY end end context 'without a block' do - let(:source) { "4.times.#{method}" } - it "doesn't register an offense" do - expect(cop.offenses.empty?).to be(true) + expect_no_offenses(<<~RUBY) + 4.times.#{method} + RUBY end end context 'called on nothing' do - let(:source) { "times.#{method} { |i| i.to_s }" } - it "doesn't register an offense" do - expect(cop.offenses.empty?).to be(true) + expect_no_offenses(<<~RUBY) + times.#{method} { |i| i.to_s } + RUBY end end end