Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add new
Performance/UnfreezeString
cop
In Ruby 2.3 or later, `String#+@` is available. This method unfreezes a string. ```ruby str = 'foo'.freeze p str.frozen? # => true p (+str).frozen? # => false ``` `String#dup` works similarly, but `+@` is faster than `dup`. See. https://gist.github.com/k0kubun/e3da77cae2c132badd386c96f2de5768 This cop recommends to use `+@` instead of `dup`.
- Loading branch information
Showing
7 changed files
with
165 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
# frozen_string_literal: true | ||
|
||
module RuboCop | ||
module Cop | ||
module Performance | ||
# In Ruby 2.3 or later, use unary plus operator to unfreeze a string | ||
# literal instead of `String#dup` and `String.new`. | ||
# Unary plus operator is faster than `String#dup`. | ||
# | ||
# Note: `String.new` (without operator) is not exactly the same as `+''`. | ||
# These differ in encoding. `String.new.encoding` is always `ASCII-8BIT`. | ||
# However, `(+'').encoding` is the same as script encoding(e.g. `UTF-8`). | ||
# So, if you expect `ASCII-8BIT` encoding, disable this cop. | ||
# | ||
# @example | ||
# # bad | ||
# ''.dup | ||
# "something".dup | ||
# String.new | ||
# String.new('') | ||
# String.new('something') | ||
# | ||
# # good | ||
# +'something' | ||
# +'' | ||
class UnfreezeString < Cop | ||
extend TargetRubyVersion | ||
|
||
minimum_target_ruby_version 2.3 | ||
|
||
MSG = 'Use unary plus to get an unfrozen string literal.'.freeze | ||
|
||
def_node_matcher :dup_string?, <<-PATTERN | ||
(send {str dstr} :dup) | ||
PATTERN | ||
|
||
def_node_matcher :string_new?, <<-PATTERN | ||
{ | ||
(send (const nil :String) :new {str dstr}) | ||
(send (const nil :String) :new) | ||
} | ||
PATTERN | ||
|
||
def on_send(node) | ||
add_offense(node) if dup_string?(node) || string_new?(node) | ||
end | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
# frozen_string_literal: true | ||
|
||
describe RuboCop::Cop::Performance::UnfreezeString, :config do | ||
subject(:cop) { described_class.new(config) } | ||
|
||
context 'TargetRubyVersion >= 2.3', :ruby23 do | ||
it 'registers an offense for an empty string with `.dup`' do | ||
expect_offense(<<-RUBY.strip_indent) | ||
"".dup | ||
^^^^^^ Use unary plus to get an unfrozen string literal. | ||
RUBY | ||
end | ||
|
||
it 'registers an offense for a string with `.dup`' do | ||
expect_offense(<<-RUBY.strip_indent) | ||
"foo".dup | ||
^^^^^^^^^ Use unary plus to get an unfrozen string literal. | ||
RUBY | ||
end | ||
|
||
it 'registers an offense for a heredoc with `.dup`' do | ||
expect_offense(<<-RUBY.strip_indent) | ||
<<TEXT.dup | ||
^^^^^^^^^^ Use unary plus to get an unfrozen string literal. | ||
foo | ||
bar | ||
TEXT | ||
RUBY | ||
end | ||
|
||
it 'registers an offense for a string that contains a string' \ | ||
'interpolation with `.dup`' do | ||
expect_offense(<<-'RUBY'.strip_indent) | ||
"foo#{bar}baz".dup | ||
^^^^^^^^^^^^^^^^^^ Use unary plus to get an unfrozen string literal. | ||
RUBY | ||
end | ||
|
||
it 'registers an offense for `String.new`' do | ||
expect_offense(<<-RUBY.strip_indent) | ||
String.new | ||
^^^^^^^^^^ Use unary plus to get an unfrozen string literal. | ||
RUBY | ||
end | ||
|
||
it 'registers an offense for `String.new` with an empty string' do | ||
expect_offense(<<-RUBY.strip_indent) | ||
String.new('') | ||
^^^^^^^^^^^^^^ Use unary plus to get an unfrozen string literal. | ||
RUBY | ||
end | ||
|
||
it 'registers an offense for `String.new` with a string' do | ||
expect_offense(<<-RUBY.strip_indent) | ||
String.new('foo') | ||
^^^^^^^^^^^^^^^^^ Use unary plus to get an unfrozen string literal. | ||
RUBY | ||
end | ||
|
||
it 'accepts an empty string with unary plus operator' do | ||
expect_no_offenses(<<-RUBY.strip_indent) | ||
+"" | ||
RUBY | ||
end | ||
|
||
it 'accepts a string with unary plus operator' do | ||
expect_no_offenses(<<-RUBY.strip_indent) | ||
+"foobar" | ||
RUBY | ||
end | ||
|
||
it 'accepts `String.new` with capacity option' do | ||
expect_no_offenses(<<-RUBY.strip_indent) | ||
String.new(capacity: 100) | ||
RUBY | ||
end | ||
end | ||
end |