From b5869123df7d993b55e89ac5ac1e2e807f3d18d7 Mon Sep 17 00:00:00 2001 From: Parker Finch Date: Tue, 15 Oct 2019 23:06:11 -0400 Subject: [PATCH] [Fix #7426] Add `always_true` to Style/FrozenStringLiteralComment This adds another style to Style/FrozenStringLiteralComment that enforces that the comment is always enabled, that is, it requires that string literals are always frozen. The default style, `always`, enforces that the comment exists but does not enforce that the comment is enabled. This also differentiates the offense message for the `always` and `always_true` styles. It changes the offense message when using the `always` style to be less specific, since the old version of the message implied that the only acceptable value of the frozen_string_literal comment was `true`. In fact, in the `always` style, either `true` or `false` is acceptable. The new `always_true` style uses that preexisting message, since when using that style it is required that the value of the frozen_string_literal comment is `true`. --- CHANGELOG.md | 4 + config/default.yml | 4 + .../cop/mixin/frozen_string_literal.rb | 6 + .../style/frozen_string_literal_comment.rb | 100 ++- manual/cops_style.md | 27 +- spec/fixtures/html_formatter/expected.html | 6 +- spec/rubocop/cli/cli_auto_gen_config_spec.rb | 16 +- spec/rubocop/cli/cli_autocorrect_spec.rb | 2 +- .../cli/cli_disable_uncorrectable_spec.rb | 10 +- spec/rubocop/cli/cli_options_spec.rb | 24 +- spec/rubocop/cli_spec.rb | 2 +- .../frozen_string_literal_comment_spec.rb | 584 +++++++++++++++++- 12 files changed, 736 insertions(+), 49 deletions(-) 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 @@

RuboCop Inspection Report

Line #1convention: - Style/FrozenStringLiteralComment: Missing magic comment # frozen_string_literal: true. + Style/FrozenStringLiteralComment: Missing magic "frozen_string_literal" comment.
class ApplicationController < ActionController::Base
@@ -461,7 +461,7 @@

RuboCop Inspection Report

Line #1convention: - Style/FrozenStringLiteralComment: Missing magic comment # frozen_string_literal: true. + Style/FrozenStringLiteralComment: Missing magic "frozen_string_literal" comment.
class BooksController < ApplicationController
@@ -625,7 +625,7 @@

RuboCop Inspection Report

Line #1convention: - Style/FrozenStringLiteralComment: Missing magic comment # frozen_string_literal: true. + Style/FrozenStringLiteralComment: Missing magic "frozen_string_literal" comment.
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