diff --git a/CHANGELOG.md b/CHANGELOG.md index 75e9faece00..9403f6e6c30 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## master (unreleased) +### New features + +* [#7426](https://github.com/rubocop-hq/rubocop/issues/7426): Add `always_true` style to Style/FrozenStringLiteralComment. ([@parkerfinch][]) + ### Bug fixes * [#7530](https://github.com/rubocop-hq/rubocop/issues/7530): Typo in `Style/TrivialAccessors`'s `AllowedMethods`. ([@movermeyer][]) diff --git a/config/default.yml b/config/default.yml index 50789ce0b1d..b85958fbce1 100644 --- a/config/default.yml +++ b/config/default.yml @@ -2754,6 +2754,10 @@ Style/FrozenStringLiteralComment: # string literal. If you run code against multiple versions of Ruby, it is # possible that this will create errors in Ruby 2.3.0+. - always + # `always_true` will add the frozen string literal comment to a file, + # similarly to the `always` style, but will also change any disabled + # comments (e.g. `# frozen_string_literal: false`) to be enabled. + - always_true # `never` will enforce that the frozen string literal comment does not # exist in a file. - never diff --git a/lib/rubocop/cop/mixin/frozen_string_literal.rb b/lib/rubocop/cop/mixin/frozen_string_literal.rb index 7251e22870e..d64f6b0a9b9 100644 --- a/lib/rubocop/cop/mixin/frozen_string_literal.rb +++ b/lib/rubocop/cop/mixin/frozen_string_literal.rb @@ -39,6 +39,12 @@ def frozen_string_literals_enabled? end end + def frozen_string_literal_specified? + leading_comment_lines.any? do |line| + MagicComment.parse(line).frozen_string_literal_specified? + end + end + def leading_comment_lines comments = processed_source.comments diff --git a/lib/rubocop/cop/style/frozen_string_literal_comment.rb b/lib/rubocop/cop/style/frozen_string_literal_comment.rb index 977a21afb79..66abefd463a 100644 --- a/lib/rubocop/cop/style/frozen_string_literal_comment.rb +++ b/lib/rubocop/cop/style/frozen_string_literal_comment.rb @@ -51,29 +51,65 @@ module Style # module Baz # # ... # end + # + # @example EnforcedStyle: always_true + # # The `always_true` style enforces that the frozen string literal + # # comment is set to `true`. This is a stricter option than `always` + # # and forces projects to use frozen string literals. + # # bad + # # frozen_string_literal: false + # + # module Baz + # # ... + # end + # + # # bad + # module Baz + # # ... + # end + # + # # good + # # frozen_string_literal: true + # + # module Bar + # # ... + # end class FrozenStringLiteralComment < Cop include ConfigurableEnforcedStyle include FrozenStringLiteral include RangeHelp - MSG = 'Missing magic comment `# frozen_string_literal: true`.' + MSG_MISSING_TRUE = 'Missing magic comment `# frozen_string_literal: '\ + 'true`.' + MSG_MISSING = 'Missing magic "frozen_string_literal" comment.' MSG_UNNECESSARY = 'Unnecessary frozen string literal comment.' + MSG_DISABLED = 'Frozen string literal comment must be set to `true`.' SHEBANG = '#!' def investigate(processed_source) return if processed_source.tokens.empty? - if frozen_string_literal_comment_exists? - check_for_no_comment(processed_source) + case style + when :never + ensure_no_comment(processed_source) + when :always_true + ensure_enabled_comment(processed_source) else - check_for_comment(processed_source) + ensure_comment(processed_source) end end def autocorrect(node) lambda do |corrector| - if style == :never + case style + when :never remove_comment(corrector, node) + when :always_true + if frozen_string_literal_specified? + enable_comment(corrector) + else + insert_comment(corrector) + end else insert_comment(corrector) end @@ -82,12 +118,27 @@ def autocorrect(node) private - def check_for_no_comment(processed_source) - unnecessary_comment_offense(processed_source) if style == :never + def ensure_no_comment(processed_source) + return unless frozen_string_literal_comment_exists? + + unnecessary_comment_offense(processed_source) end - def check_for_comment(processed_source) - offense(processed_source) unless style == :never + def ensure_comment(processed_source) + return if frozen_string_literal_comment_exists? + + missing_offense(processed_source) + end + + def ensure_enabled_comment(processed_source) + if frozen_string_literal_specified? + return if frozen_string_literals_enabled? + + # The comment exists, but is not enabled. + disabled_offense(processed_source) + else # The comment doesn't exist at all. + missing_true_offense(processed_source) + end end def last_special_comment(processed_source) @@ -111,11 +162,22 @@ def frozen_string_literal_comment(processed_source) end end - def offense(processed_source) + def missing_offense(processed_source) + last_special_comment = last_special_comment(processed_source) + range = source_range(processed_source.buffer, 0, 0) + + add_offense(last_special_comment, + location: range, + message: MSG_MISSING) + end + + def missing_true_offense(processed_source) last_special_comment = last_special_comment(processed_source) range = source_range(processed_source.buffer, 0, 0) - add_offense(last_special_comment, location: range) + add_offense(last_special_comment, + location: range, + message: MSG_MISSING_TRUE) end def unnecessary_comment_offense(processed_source) @@ -127,11 +189,27 @@ def unnecessary_comment_offense(processed_source) message: MSG_UNNECESSARY) end + def disabled_offense(processed_source) + frozen_string_literal_comment = + frozen_string_literal_comment(processed_source) + + add_offense(frozen_string_literal_comment, + location: frozen_string_literal_comment.pos, + message: MSG_DISABLED) + end + def remove_comment(corrector, node) corrector.remove(range_with_surrounding_space(range: node.pos, side: :right)) end + def enable_comment(corrector) + comment = frozen_string_literal_comment(processed_source) + + corrector.replace(line_range(comment.line), + FROZEN_STRING_LITERAL_ENABLED) + end + def insert_comment(corrector) comment = last_special_comment(processed_source) diff --git a/manual/cops_style.md b/manual/cops_style.md index 483d0f0568a..02460a8aac3 100644 --- a/manual/cops_style.md +++ b/manual/cops_style.md @@ -2328,12 +2328,37 @@ module Baz # ... end ``` +#### EnforcedStyle: always_true + +```ruby +# The `always_true` style enforces that the frozen string literal +# comment is set to `true`. This is a stricter option than `always` +# and forces projects to use frozen string literals. +# bad +# frozen_string_literal: false + +module Baz + # ... +end + +# bad +module Baz + # ... +end + +# good +# frozen_string_literal: true + +module Bar + # ... +end +``` ### Configurable attributes Name | Default value | Configurable values --- | --- | --- -EnforcedStyle | `always` | `always`, `never` +EnforcedStyle | `always` | `always`, `always_true`, `never` ## Style/GlobalVars diff --git a/spec/fixtures/html_formatter/expected.html b/spec/fixtures/html_formatter/expected.html index 6b0a6641d6e..6c59a8e0691 100644 --- a/spec/fixtures/html_formatter/expected.html +++ b/spec/fixtures/html_formatter/expected.html @@ -418,7 +418,7 @@
class ApplicationController < ActionController::Base
@@ -461,7 +461,7 @@ class BooksController < ApplicationController
@@ -625,7 +625,7 @@ class Book < ActiveRecord::Base
diff --git a/spec/rubocop/cli/cli_auto_gen_config_spec.rb b/spec/rubocop/cli/cli_auto_gen_config_spec.rb
index 52ff9b035a9..ffdbd9c60b7 100644
--- a/spec/rubocop/cli/cli_auto_gen_config_spec.rb
+++ b/spec/rubocop/cli/cli_auto_gen_config_spec.rb
@@ -178,7 +178,7 @@ def f
# Offense count: 1
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle.
- # SupportedStyles: always, never
+ # SupportedStyles: always, always_true, never
Style/FrozenStringLiteralComment:
Exclude:
- 'example.rb'
@@ -226,7 +226,7 @@ def f
# Offense count: 1
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle.
- # SupportedStyles: always, never
+ # SupportedStyles: always, always_true, never
Style/FrozenStringLiteralComment:
Exclude:
- 'example.rb'
@@ -276,7 +276,7 @@ def f
# Offense count: 1
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle.
- # SupportedStyles: always, never
+ # SupportedStyles: always, always_true, never
Style/FrozenStringLiteralComment:
Exclude:
- 'example.rb'
@@ -369,7 +369,7 @@ def f
'# Offense count: 1',
'# Cop supports --auto-correct.',
'# Configuration parameters: EnforcedStyle.',
- '# SupportedStyles: always, never',
+ '# SupportedStyles: always, always_true, never',
'Style/FrozenStringLiteralComment:',
' Exclude:',
" - 'example1.rb'",
@@ -411,7 +411,7 @@ def f
# Offense count: 1
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle.
- # SupportedStyles: always, never
+ # SupportedStyles: always, always_true, never
Style/FrozenStringLiteralComment:
Exclude:
- 'example1.rb'
@@ -459,7 +459,7 @@ def f
# Offense count: 1
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle.
- # SupportedStyles: always, never
+ # SupportedStyles: always, always_true, never
Style/FrozenStringLiteralComment:
Exclude:
- 'example1.rb'
@@ -1006,7 +1006,7 @@ def function(arg1, arg2, arg3, arg4, arg5, arg6, arg7)
# Offense count: 3
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle.
- # SupportedStyles: always, never
+ # SupportedStyles: always, always_true, never
Style/FrozenStringLiteralComment:
Enabled: false
@@ -1027,7 +1027,7 @@ def function(arg1, arg2, arg3, arg4, arg5, arg6, arg7)
# Offense count: 4
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle.
- # SupportedStyles: always, never
+ # SupportedStyles: always, always_true, never
Style/FrozenStringLiteralComment:
Exclude:
- 'example1.rb'
diff --git a/spec/rubocop/cli/cli_autocorrect_spec.rb b/spec/rubocop/cli/cli_autocorrect_spec.rb
index cd92749b715..beaa99f2a1d 100644
--- a/spec/rubocop/cli/cli_autocorrect_spec.rb
+++ b/spec/rubocop/cli/cli_autocorrect_spec.rb
@@ -765,7 +765,7 @@ def func
expect(cli.run(%w[--auto-correct --format simple])).to eq(1)
expect($stdout.string).to eq(<<~RESULT)
== example.rb ==
- C: 1: 1: [Corrected] Style/FrozenStringLiteralComment: Missing magic comment # frozen_string_literal: true.
+ C: 1: 1: [Corrected] Style/FrozenStringLiteralComment: Missing magic "frozen_string_literal" comment.
C: 2: 1: [Corrected] Layout/EmptyLineAfterMagicComment: Add an empty line after magic comments.
C: 3: 1: Style/Documentation: Missing top-level class documentation comment.
W: 4: 3: [Corrected] Lint/RedundantCopDisableDirective: Unnecessary disabling of Metrics/MethodLength.
diff --git a/spec/rubocop/cli/cli_disable_uncorrectable_spec.rb b/spec/rubocop/cli/cli_disable_uncorrectable_spec.rb
index 8ee383c199b..3e1aa1fae14 100644
--- a/spec/rubocop/cli/cli_disable_uncorrectable_spec.rb
+++ b/spec/rubocop/cli/cli_disable_uncorrectable_spec.rb
@@ -16,7 +16,7 @@
expect($stderr.string).to eq('')
expect($stdout.string).to eq(<<~OUTPUT)
== example.rb ==
- C: 1: 1: [Corrected] Style/FrozenStringLiteralComment: Missing magic comment # frozen_string_literal: true.
+ C: 1: 1: [Corrected] Style/FrozenStringLiteralComment: Missing magic "frozen_string_literal" comment.
C: 1: 7: [Corrected] Layout/SpaceAroundOperators: Surrounding space missing for operator ==.
C: 2: 1: [Corrected] Layout/EmptyLineAfterMagicComment: Add an empty line after magic comments.
@@ -40,7 +40,7 @@ def is_example
expect($stderr.string).to eq('')
expect($stdout.string).to eq(<<~OUTPUT)
== example.rb ==
- C: 1: 1: [Corrected] Style/FrozenStringLiteralComment: Missing magic comment # frozen_string_literal: true.
+ C: 1: 1: [Corrected] Style/FrozenStringLiteralComment: Missing magic "frozen_string_literal" comment.
C: 1: 5: [Todo] Naming/PredicateName: Rename is_example to example?.
C: 2: 1: [Corrected] Layout/EmptyLineAfterMagicComment: Add an empty line after magic comments.
@@ -68,7 +68,7 @@ def is_example # rubocop:todo Naming/PredicateName
expect($stderr.string).to eq('')
expect($stdout.string).to eq(<<~OUTPUT)
== example.rb ==
- C: 1: 1: [Corrected] Style/FrozenStringLiteralComment: Missing magic comment # frozen_string_literal: true.
+ C: 1: 1: [Corrected] Style/FrozenStringLiteralComment: Missing magic "frozen_string_literal" comment.
C: 1: 4: [Todo] Style/IpAddresses: Do not hardcode IP addresses.
C: 1: 15: [Todo] Style/IpAddresses: Do not hardcode IP addresses.
C: 2: 1: [Corrected] Layout/EmptyLineAfterMagicComment: Add an empty line after magic comments.
@@ -111,7 +111,7 @@ def choose_move(who_to_move)
expect($stderr.string).to eq('')
expect($stdout.string).to eq(<<~OUTPUT)
== example.rb ==
- C: 1: 1: [Corrected] Style/FrozenStringLiteralComment: Missing magic comment # frozen_string_literal: true.
+ C: 1: 1: [Corrected] Style/FrozenStringLiteralComment: Missing magic "frozen_string_literal" comment.
C: 2: 1: [Corrected] Layout/EmptyLineAfterMagicComment: Add an empty line after magic comments.
C: 3: 3: [Todo] Metrics/AbcSize: Assignment Branch Condition size for choose_move is too high. [<8, 12, 6> 15.62/15]
C: 3: 3: [Todo] Metrics/CyclomaticComplexity: Cyclomatic complexity for choose_move is too high. [7/6]
@@ -170,7 +170,7 @@ def long_method_name(_taking, _a_few, _parameters, _resulting_in_a_long_line)
expect($stdout.string).to eq(<<~OUTPUT)
== example.rb ==
C: 1: 1: [Todo] Metrics/MethodLength: Method has too many lines. [2/1]
- C: 1: 1: [Corrected] Style/FrozenStringLiteralComment: Missing magic comment # frozen_string_literal: true.
+ C: 1: 1: [Corrected] Style/FrozenStringLiteralComment: Missing magic "frozen_string_literal" comment.
C: 3: 1: [Corrected] Layout/EmptyLineAfterMagicComment: Add an empty line after magic comments.
1 file inspected, 3 offenses detected, 3 offenses corrected
diff --git a/spec/rubocop/cli/cli_options_spec.rb b/spec/rubocop/cli/cli_options_spec.rb
index ed21953871c..feb6d54c200 100644
--- a/spec/rubocop/cli/cli_options_spec.rb
+++ b/spec/rubocop/cli/cli_options_spec.rb
@@ -531,7 +531,7 @@ class SomeCop < Cop
expect(cli.run(['--format', 'emacs', '--display-cop-names',
'example1.rb'])).to eq(1)
expect($stdout.string).to eq(<<~RESULT)
- #{file}:1:1: C: Style/FrozenStringLiteralComment: Missing magic comment `# frozen_string_literal: true`.
+ #{file}:1:1: C: Style/FrozenStringLiteralComment: Missing magic "frozen_string_literal" comment.
#{file}:1:8: W: Lint/RedundantCopDisableDirective: Unnecessary disabling of `Style/NumericLiterals`.
#{file}:1:41: C: Layout/TrailingWhitespace: Trailing whitespace detected.
RESULT
@@ -542,7 +542,7 @@ class SomeCop < Cop
expect(cli.run(['--format', 'emacs', '--no-display-cop-names',
'example1.rb'])).to eq(1)
expect($stdout.string).to eq(<<~RESULT)
- #{file}:1:1: C: Missing magic comment `# frozen_string_literal: true`.
+ #{file}:1:1: C: Missing magic "frozen_string_literal" comment.
#{file}:1:8: W: Unnecessary disabling of `Style/NumericLiterals`.
#{file}:1:41: C: Trailing whitespace detected.
RESULT
@@ -561,7 +561,7 @@ class SomeCop < Cop
expect(cli.run(['--format', 'emacs', '--display-cop-names',
'example1.rb'])).to eq(1)
expect($stdout.string).to eq(<<~RESULT)
- #{file}:1:1: C: Style/FrozenStringLiteralComment: Missing magic comment `# frozen_string_literal: true`.
+ #{file}:1:1: C: Style/FrozenStringLiteralComment: Missing magic "frozen_string_literal" comment.
#{file}:1:8: W: Lint/RedundantCopDisableDirective: Unnecessary disabling of `Style/NumericLiterals`.
#{file}:1:41: C: Layout/TrailingWhitespace: Trailing whitespace detected.
RESULT
@@ -571,7 +571,7 @@ class SomeCop < Cop
it 'does not show cop names' do
expect(cli.run(['--format', 'emacs', 'example1.rb'])).to eq(1)
expect($stdout.string).to eq(<<~RESULT)
- #{file}:1:1: C: Missing magic comment `# frozen_string_literal: true`.
+ #{file}:1:1: C: Missing magic "frozen_string_literal" comment.
#{file}:1:8: W: Unnecessary disabling of `Style/NumericLiterals`.
#{file}:1:41: C: Trailing whitespace detected.
RESULT
@@ -593,7 +593,7 @@ class SomeCop < Cop
expect(cli.run(['--format', 'emacs', '--extra-details',
'example1.rb'])).to eq(1)
expect($stdout.string).to eq(<<~RESULT)
- #{file}:1:1: C: Style/FrozenStringLiteralComment: Missing magic comment `# frozen_string_literal: true`.
+ #{file}:1:1: C: Style/FrozenStringLiteralComment: Missing magic "frozen_string_literal" comment.
#{file}:1:8: W: Lint/RedundantCopDisableDirective: Unnecessary disabling of `Style/NumericLiterals`.
#{file}:1:47: C: Layout/TrailingWhitespace: Trailing whitespace detected. Trailing space is just sloppy.
RESULT
@@ -805,7 +805,7 @@ def full_description_of_cop(cop)
expect($stdout.string)
.to include(<<~RESULT)
== #{target_file} ==
- C: 1: 1: Style/FrozenStringLiteralComment: Missing magic comment # frozen_string_literal: true.
+ C: 1: 1: Style/FrozenStringLiteralComment: Missing magic "frozen_string_literal" comment.
C: 1: 81: Metrics/LineLength: Line is too long. [90/80]
1 file inspected, 2 offenses detected
@@ -890,7 +890,7 @@ def badName
.to eq(1)
expect($stdout.string).to eq([
'example1.rb:1:1: C: Style/FrozenStringLiteralComment: ' \
- 'Missing magic comment # frozen_string_literal: true.',
+ 'Missing magic "frozen_string_literal" comment.',
'x= 0 ',
'^',
'example1.rb:1:2: C: Layout/SpaceAroundOperators: ' \
@@ -931,7 +931,7 @@ def badName
' puts',
'^^^',
'example3.rb:1:1: C: Style/FrozenStringLiteralComment: ' \
- 'Missing magic comment # frozen_string_literal: true.',
+ 'Missing magic "frozen_string_literal" comment.',
'def badName',
'^',
'example3.rb:1:5: C: Naming/MethodName: ' \
@@ -1044,9 +1044,9 @@ def finished(processed_files)
cli.run(['--format', 'simple', '--format', 'emacs', 'example.rb'])
expect($stdout.string).to include(<<~RESULT)
== #{target_file} ==
- C: 1: 1: Style/FrozenStringLiteralComment: Missing magic comment # frozen_string_literal: true.
+ C: 1: 1: Style/FrozenStringLiteralComment: Missing magic "frozen_string_literal" comment.
C: 1: 81: Metrics/LineLength: Line is too long. [90/80]
- #{abs(target_file)}:1:1: C: Style/FrozenStringLiteralComment: Missing magic comment `# frozen_string_literal: true`.
+ #{abs(target_file)}:1:1: C: Style/FrozenStringLiteralComment: Missing magic "frozen_string_literal" comment.
#{abs(target_file)}:1:81: C: Metrics/LineLength: Line is too long. [90/80]
RESULT
end
@@ -1071,7 +1071,7 @@ def finished(processed_files)
expect($stdout.string).to eq(<<~RESULT)
== #{target_file} ==
- C: 1: 1: Style/FrozenStringLiteralComment: Missing magic comment # frozen_string_literal: true.
+ C: 1: 1: Style/FrozenStringLiteralComment: Missing magic "frozen_string_literal" comment.
C: 1: 81: Metrics/LineLength: Line is too long. [90/80]
1 file inspected, 2 offenses detected
@@ -1079,7 +1079,7 @@ def finished(processed_files)
expect(File.read('emacs_output.txt'))
.to eq(<<~RESULT)
- #{abs(target_file)}:1:1: C: Style/FrozenStringLiteralComment: Missing magic comment `# frozen_string_literal: true`.
+ #{abs(target_file)}:1:1: C: Style/FrozenStringLiteralComment: Missing magic "frozen_string_literal" comment.
#{abs(target_file)}:1:81: C: Metrics/LineLength: Line is too long. [90/80]
RESULT
end
diff --git a/spec/rubocop/cli_spec.rb b/spec/rubocop/cli_spec.rb
index b441e68d1e5..d466740204c 100644
--- a/spec/rubocop/cli_spec.rb
+++ b/spec/rubocop/cli_spec.rb
@@ -79,7 +79,7 @@
.to eq(<<~RESULT)
== example.rb ==
C: 1: 1: Layout/EndOfLine: Carriage return character detected.
- C: 1: 1: Style/FrozenStringLiteralComment: Missing magic comment # frozen_string_literal: true.
+ C: 1: 1: Style/FrozenStringLiteralComment: Missing magic "frozen_string_literal" comment.
1 file inspected, 2 offenses detected
RESULT
diff --git a/spec/rubocop/cop/style/frozen_string_literal_comment_spec.rb b/spec/rubocop/cop/style/frozen_string_literal_comment_spec.rb
index 85cce05916d..a9be64f635d 100644
--- a/spec/rubocop/cop/style/frozen_string_literal_comment_spec.rb
+++ b/spec/rubocop/cop/style/frozen_string_literal_comment_spec.rb
@@ -34,7 +34,7 @@
it 'registers an offense for arbitrary tokens' do
expect_offense(<<~RUBY)
# frozen_string_literal: token
- ^ Missing magic comment `# frozen_string_literal: true`.
+ ^ Missing magic "frozen_string_literal" comment.
puts 1
RUBY
end
@@ -43,7 +43,7 @@
'on the top line' do
expect_offense(<<~RUBY)
puts 1
- ^ Missing magic comment `# frozen_string_literal: true`.
+ ^ Missing magic "frozen_string_literal" comment.
RUBY
end
@@ -51,7 +51,7 @@
'under a shebang' do
expect_offense(<<~RUBY)
#!/usr/bin/env ruby
- ^ Missing magic comment `# frozen_string_literal: true`.
+ ^ Missing magic "frozen_string_literal" comment.
puts 1
RUBY
end
@@ -76,7 +76,7 @@
'under an encoding comment' do
expect_offense(<<~RUBY)
# encoding: utf-8
- ^ Missing magic comment `# frozen_string_literal: true`.
+ ^ Missing magic "frozen_string_literal" comment.
puts 1
RUBY
end
@@ -101,7 +101,7 @@
'under a shebang and an encoding comment' do
expect_offense(<<~RUBY)
#!/usr/bin/env ruby
- ^ Missing magic comment `# frozen_string_literal: true`.
+ ^ Missing magic "frozen_string_literal" comment.
# encoding: utf-8
puts 1
RUBY
@@ -160,7 +160,7 @@
'when there is only a shebang' do
expect_offense(<<~RUBY)
#!/usr/bin/env ruby
- ^ Missing magic comment `# frozen_string_literal: true`.
+ ^ Missing magic "frozen_string_literal" comment.
RUBY
end
@@ -488,7 +488,7 @@
RUBY
end
- it 'removes a dsabled frozen string literal below an encoding comment' do
+ it 'removes a disabled frozen string literal below an encoding comment' do
new_source = autocorrect_source(<<~RUBY)
# encoding: utf-8
# frozen_string_literal: false
@@ -566,4 +566,574 @@
end
end
end
+
+ context 'always_true' do
+ let(:cop_config) do
+ { 'Enabled' => true,
+ 'EnforcedStyle' => 'always_true' }
+ end
+
+ it 'accepts an empty source' do
+ expect_no_offenses('')
+ end
+
+ it 'accepts a source with no tokens' do
+ expect_no_offenses(' ')
+ end
+
+ it 'accepts a frozen string literal on the top line' do
+ expect_no_offenses(<<~RUBY)
+ # frozen_string_literal: true
+ puts 1
+ RUBY
+ end
+
+ it 'registers an offense for a disabled frozen string literal on the top ' \
+ 'line' do
+ expect_offense(<<~RUBY)
+ # frozen_string_literal: false
+ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Frozen string literal comment must be set to `true`.
+ puts 1
+ RUBY
+ end
+
+ it 'registers an offense for arbitrary tokens' do
+ expect_offense(<<~RUBY)
+ # frozen_string_literal: token
+ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Frozen string literal comment must be set to `true`.
+ puts 1
+ RUBY
+ end
+
+ it 'registers an offense for not having a frozen string literal comment ' \
+ 'on the top line' do
+ expect_offense(<<~RUBY)
+ puts 1
+ ^ Missing magic comment `# frozen_string_literal: true`.
+ RUBY
+ end
+
+ it 'registers an offense for not having a frozen string literal comment ' \
+ 'under a shebang' do
+ expect_offense(<<~RUBY)
+ #!/usr/bin/env ruby
+ ^ Missing magic comment `# frozen_string_literal: true`.
+ puts 1
+ RUBY
+ end
+
+ it 'accepts a frozen string literal below a shebang comment' do
+ expect_no_offenses(<<~RUBY)
+ #!/usr/bin/env ruby
+ # frozen_string_literal: true
+ puts 1
+ RUBY
+ end
+
+ it 'registers an offense for a disabled frozen string literal below ' \
+ 'a shebang comment' do
+ expect_offense(<<~RUBY)
+ #!/usr/bin/env ruby
+ # frozen_string_literal: false
+ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Frozen string literal comment must be set to `true`.
+ puts 1
+ RUBY
+ end
+
+ it 'registers an offense for not having a frozen string literal comment ' \
+ 'under an encoding comment' do
+ expect_offense(<<~RUBY)
+ # encoding: utf-8
+ ^ Missing magic comment `# frozen_string_literal: true`.
+ puts 1
+ RUBY
+ end
+
+ it 'accepts a frozen string literal below an encoding comment' do
+ expect_no_offenses(<<~RUBY)
+ # encoding: utf-8
+ # frozen_string_literal: true
+ puts 1
+ RUBY
+ end
+
+ it 'registers an offense for a disabled frozen string literal ' \
+ 'below an encoding comment' do
+ expect_offense(<<~RUBY)
+ # encoding: utf-8
+ # frozen_string_literal: false
+ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Frozen string literal comment must be set to `true`.
+ puts 1
+ RUBY
+ end
+
+ it 'registers an offense for not having a frozen string literal comment ' \
+ 'under a shebang and an encoding comment' do
+ expect_offense(<<~RUBY)
+ #!/usr/bin/env ruby
+ ^ Missing magic comment `# frozen_string_literal: true`.
+ # encoding: utf-8
+ puts 1
+ RUBY
+ end
+
+ it 'accepts a frozen string literal comment below shebang and encoding ' \
+ 'comments' do
+ expect_no_offenses(<<~RUBY)
+ #!/usr/bin/env ruby
+ # encoding: utf-8
+ # frozen_string_literal: true
+ puts 1
+ RUBY
+ end
+
+ it 'registers an offense for a disabled frozen string literal comment ' \
+ 'below shebang and encoding comments' do
+ expect_offense(<<~RUBY)
+ #!/usr/bin/env ruby
+ # encoding: utf-8
+ # frozen_string_literal: false
+ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Frozen string literal comment must be set to `true`.
+ puts 1
+ RUBY
+ end
+
+ it 'accepts a frozen string literal comment below shebang above an ' \
+ 'encoding comments' do
+ expect_no_offenses(<<~RUBY)
+ #!/usr/bin/env ruby
+ # frozen_string_literal: true
+ # encoding: utf-8
+ puts 1
+ RUBY
+ end
+
+ it 'registers an offense for a disabled frozen string literal ' \
+ 'comment below shebang above an encoding comments' do
+ expect_offense(<<~RUBY)
+ #!/usr/bin/env ruby
+ # frozen_string_literal: false
+ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Frozen string literal comment must be set to `true`.
+ # encoding: utf-8
+ puts 1
+ RUBY
+ end
+
+ it 'accepts an emacs style combined magic comment' do
+ expect_no_offenses(<<~RUBY)
+ #!/usr/bin/env ruby
+ # -*- encoding: UTF-8; frozen_string_literal: true -*-
+ # encoding: utf-8
+ puts 1
+ RUBY
+ end
+
+ it 'registers an offence for not having a frozen string literal comment ' \
+ 'when there is only a shebang' do
+ expect_offense(<<~RUBY)
+ #!/usr/bin/env ruby
+ ^ Missing magic comment `# frozen_string_literal: true`.
+ RUBY
+ end
+
+ context 'auto-correct' do
+ it 'adds a frozen string literal comment to the first line if one is ' \
+ 'missing' do
+ new_source = autocorrect_source(<<~RUBY)
+ puts 1
+ RUBY
+
+ expect(new_source).to eq(<<~RUBY)
+ # frozen_string_literal: true
+ puts 1
+ RUBY
+ end
+
+ it 'enables a frozen string literal comment on the first line when ' \
+ 'one is disabled' do
+ new_source = autocorrect_source(<<~RUBY)
+ # frozen_string_literal: false
+ puts 1
+ RUBY
+
+ expect(new_source).to eq(<<~RUBY)
+ # frozen_string_literal: true
+ puts 1
+ RUBY
+ end
+
+ it 'enables a frozen string literal comment on the first line when ' \
+ 'one is invalid' do
+ new_source = autocorrect_source(<<~RUBY)
+ # frozen_string_literal: foobar
+ puts 1
+ RUBY
+
+ expect(new_source).to eq(<<~RUBY)
+ # frozen_string_literal: true
+ puts 1
+ RUBY
+ end
+
+ it 'adds a frozen string literal comment to the first line if one is ' \
+ 'missing and handles extra spacing' do
+ new_source = autocorrect_source(<<~RUBY)
+
+ puts 1
+ RUBY
+
+ expect(new_source).to eq(<<~RUBY)
+ # frozen_string_literal: true
+
+ puts 1
+ RUBY
+ end
+
+ it 'enables a frozen string literal comment on the first line when ' \
+ 'one is disabled and handles extra spacing' do
+ new_source = autocorrect_source(<<~RUBY)
+ # frozen_string_literal: false
+
+ puts 1
+ RUBY
+
+ expect(new_source).to eq(<<~RUBY)
+ # frozen_string_literal: true
+
+ puts 1
+ RUBY
+ end
+
+ it 'enables a frozen string literal comment on the first line when ' \
+ 'one is invalid and handles extra spacing' do
+ new_source = autocorrect_source(<<~RUBY)
+ # frozen_string_literal: foobar
+
+ puts 1
+ RUBY
+
+ expect(new_source).to eq(<<~RUBY)
+ # frozen_string_literal: true
+
+ puts 1
+ RUBY
+ end
+
+ it 'adds a frozen string literal comment after a shebang' do
+ new_source = autocorrect_source(<<~RUBY)
+ #!/usr/bin/env ruby
+ puts 1
+ RUBY
+
+ expect(new_source).to eq(<<~RUBY)
+ #!/usr/bin/env ruby
+ # frozen_string_literal: true
+ puts 1
+ RUBY
+ end
+
+ it 'enables a frozen string literal comment after a shebang when' \
+ 'one is disabled' do
+ new_source = autocorrect_source(<<~RUBY)
+ #!/usr/bin/env ruby
+ # frozen_string_literal: false
+ puts 1
+ RUBY
+
+ expect(new_source).to eq(<<~RUBY)
+ #!/usr/bin/env ruby
+ # frozen_string_literal: true
+ puts 1
+ RUBY
+ end
+
+ it 'enables a frozen string literal comment after a shebang when' \
+ 'one is invalid' do
+ new_source = autocorrect_source(<<~RUBY)
+ #!/usr/bin/env ruby
+ # frozen_string_literal: foobar
+ puts 1
+ RUBY
+
+ expect(new_source).to eq(<<~RUBY)
+ #!/usr/bin/env ruby
+ # frozen_string_literal: true
+ puts 1
+ RUBY
+ end
+
+ it 'adds a frozen string literal comment after an encoding comment' do
+ new_source = autocorrect_source(<<~RUBY)
+ # encoding: utf-8
+ puts 1
+ RUBY
+
+ expect(new_source).to eq(<<~RUBY)
+ # encoding: utf-8
+ # frozen_string_literal: true
+ puts 1
+ RUBY
+ end
+
+ it 'enables a frozen string literal comment after an encoding comment' \
+ 'when one is disabled' do
+ new_source = autocorrect_source(<<~RUBY)
+ # encoding: utf-8
+ # frozen_string_literal: false
+ puts 1
+ RUBY
+
+ expect(new_source).to eq(<<~RUBY)
+ # encoding: utf-8
+ # frozen_string_literal: true
+ puts 1
+ RUBY
+ end
+
+ it 'enables a frozen string literal comment after an encoding comment' \
+ 'when one is invalid' do
+ new_source = autocorrect_source(<<~RUBY)
+ # encoding: utf-8
+ # frozen_string_literal: foobar
+ puts 1
+ RUBY
+
+ expect(new_source).to eq(<<~RUBY)
+ # encoding: utf-8
+ # frozen_string_literal: true
+ puts 1
+ RUBY
+ end
+
+ it 'adds a frozen string literal comment after a shebang and encoding ' \
+ 'comment' do
+ new_source = autocorrect_source(<<~RUBY)
+ #!/usr/bin/env ruby
+ # encoding: utf-8
+ puts 1
+ RUBY
+
+ expect(new_source).to eq(<<~RUBY)
+ #!/usr/bin/env ruby
+ # encoding: utf-8
+ # frozen_string_literal: true
+ puts 1
+ RUBY
+ end
+
+ it 'enables a frozen string literal comment after a shebang and ' \
+ 'encoding comment when one is disabled' do
+ new_source = autocorrect_source(<<~RUBY)
+ #!/usr/bin/env ruby
+ # encoding: utf-8
+ # frozen_string_literal: false
+ puts 1
+ RUBY
+
+ expect(new_source).to eq(<<~RUBY)
+ #!/usr/bin/env ruby
+ # encoding: utf-8
+ # frozen_string_literal: true
+ puts 1
+ RUBY
+ end
+
+ it 'enables a frozen string literal comment after a shebang and ' \
+ 'encoding comment when one is invalid' do
+ new_source = autocorrect_source(<<~RUBY)
+ #!/usr/bin/env ruby
+ # encoding: utf-8
+ # frozen_string_literal: foobar
+ puts 1
+ RUBY
+
+ expect(new_source).to eq(<<~RUBY)
+ #!/usr/bin/env ruby
+ # encoding: utf-8
+ # frozen_string_literal: true
+ puts 1
+ RUBY
+ end
+
+ it 'adds a frozen string literal comment after a shebang and encoding ' \
+ 'comment when there is an empty line before the code' do
+ new_source = autocorrect_source(<<~RUBY)
+ #!/usr/bin/env ruby
+ # encoding: utf-8
+
+ puts 1
+ RUBY
+
+ expect(new_source).to eq(<<~RUBY)
+ #!/usr/bin/env ruby
+ # encoding: utf-8
+ # frozen_string_literal: true
+
+ puts 1
+ RUBY
+ end
+
+ it 'enables a frozen string literal comment after a shebang and ' \
+ 'encoding comment when there is an empty line before the code and ' \
+ 'there is a disabled frozen string literal comment' do
+ new_source = autocorrect_source(<<~RUBY)
+ #!/usr/bin/env ruby
+ # encoding: utf-8
+ # frozen_string_literal: false
+
+ puts 1
+ RUBY
+
+ expect(new_source).to eq(<<~RUBY)
+ #!/usr/bin/env ruby
+ # encoding: utf-8
+ # frozen_string_literal: true
+
+ puts 1
+ RUBY
+ end
+
+ it 'enables a frozen string literal comment after a shebang and ' \
+ 'encoding comment when there is an empty line before the code and ' \
+ 'there is an invalid frozen string literal comment' do
+ new_source = autocorrect_source(<<~RUBY)
+ #!/usr/bin/env ruby
+ # encoding: utf-8
+ # frozen_string_literal: foobar
+
+ puts 1
+ RUBY
+
+ expect(new_source).to eq(<<~RUBY)
+ #!/usr/bin/env ruby
+ # encoding: utf-8
+ # frozen_string_literal: true
+
+ puts 1
+ RUBY
+ end
+
+ it 'adds a frozen string literal comment after an encoding comment ' \
+ 'when there is an empty line before the code' do
+ new_source = autocorrect_source(<<~RUBY)
+ # encoding: utf-8
+
+ puts 1
+ RUBY
+
+ expect(new_source).to eq(<<~RUBY)
+ # encoding: utf-8
+ # frozen_string_literal: true
+
+ puts 1
+ RUBY
+ end
+
+ it 'enables a frozen string literal comment after an encoding comment ' \
+ 'when disabled and there is an empty line before the code' do
+ new_source = autocorrect_source(<<~RUBY)
+ # encoding: utf-8
+ # frozen_string_literal: false
+
+ puts 1
+ RUBY
+
+ expect(new_source).to eq(<<~RUBY)
+ # encoding: utf-8
+ # frozen_string_literal: true
+
+ puts 1
+ RUBY
+ end
+
+ it 'enables a frozen string literal comment after an encoding comment ' \
+ 'when invalid and there is an empty line before the code' do
+ new_source = autocorrect_source(<<~RUBY)
+ # encoding: utf-8
+ # frozen_string_literal: foobar
+
+ puts 1
+ RUBY
+
+ expect(new_source).to eq(<<~RUBY)
+ # encoding: utf-8
+ # frozen_string_literal: true
+
+ puts 1
+ RUBY
+ end
+
+ it 'adds a frozen string literal comment after a shebang when there is ' \
+ 'only a shebang' do
+ new_source = autocorrect_source(<<~RUBY)
+ #!/usr/bin/env ruby
+ RUBY
+
+ expect(new_source).to eq(<<~RUBY)
+ #!/usr/bin/env ruby
+ # frozen_string_literal: true
+ RUBY
+ end
+
+ it 'enables a frozen string literal comment after a shebang ' \
+ 'when disabled and there is only a shebang' do
+ new_source = autocorrect_source(<<~RUBY)
+ #!/usr/bin/env ruby
+ # frozen_string_literal: false
+ RUBY
+
+ expect(new_source).to eq(<<~RUBY)
+ #!/usr/bin/env ruby
+ # frozen_string_literal: true
+ RUBY
+ end
+
+ it 'enables a frozen string literal comment after a shebang ' \
+ 'when invalid and there is only a shebang' do
+ new_source = autocorrect_source(<<~RUBY)
+ #!/usr/bin/env ruby
+ # frozen_string_literal: foo
+ RUBY
+
+ expect(new_source).to eq(<<~RUBY)
+ #!/usr/bin/env ruby
+ # frozen_string_literal: true
+ RUBY
+ end
+
+ it 'enables a disabled frozen string literal comment ' \
+ 'below shebang above an encoding comments' do
+ new_source = autocorrect_source(<<~RUBY)
+ #!/usr/bin/env ruby
+ # frozen_string_literal: false
+ # encoding: utf-8
+ puts 1
+ RUBY
+
+ expect(new_source).to eq(<<~RUBY)
+ #!/usr/bin/env ruby
+ # frozen_string_literal: true
+ # encoding: utf-8
+ puts 1
+ RUBY
+ end
+
+ it 'enables an invalid frozen string literal comment ' \
+ 'below shebang above an encoding comments' do
+ new_source = autocorrect_source(<<~RUBY)
+ #!/usr/bin/env ruby
+ # frozen_string_literal: foobar
+ # encoding: utf-8
+ puts 1
+ RUBY
+
+ expect(new_source).to eq(<<~RUBY)
+ #!/usr/bin/env ruby
+ # frozen_string_literal: true
+ # encoding: utf-8
+ puts 1
+ RUBY
+ end
+ end
+ end
end