diff --git a/changelog/new_support_ignored_methods_for_style_format_string_token.md b/changelog/new_support_ignored_methods_for_style_format_string_token.md new file mode 100644 index 000000000000..2692a3d8e7d4 --- /dev/null +++ b/changelog/new_support_ignored_methods_for_style_format_string_token.md @@ -0,0 +1 @@ +* [#7452](https://github.com/rubocop-hq/rubocop/issues/7452): Support `IgnoredMethods` option for `Style/FormatStringToken`. ([@koic][]) diff --git a/config/default.yml b/config/default.yml index 04c7fb7a638e..0f59b9cd8432 100644 --- a/config/default.yml +++ b/config/default.yml @@ -3355,6 +3355,7 @@ Style/FormatStringToken: MaxUnannotatedPlaceholdersAllowed: 1 VersionAdded: '0.49' VersionChanged: '1.0' + IgnoredMethods: [] Style/FrozenStringLiteralComment: Description: >- diff --git a/lib/rubocop/cop/style/format_string_token.rb b/lib/rubocop/cop/style/format_string_token.rb index d17d8505d242..9e9d8138ec1e 100644 --- a/lib/rubocop/cop/style/format_string_token.rb +++ b/lib/rubocop/cop/style/format_string_token.rb @@ -11,6 +11,8 @@ module Style # The reason is that _unannotated_ format is very similar # to encoded URLs or Date/Time formatting strings. # + # This cop can be customized ignored methods with `IgnoredMethods`. + # # @example EnforcedStyle: annotated (default) # # # bad @@ -58,12 +60,18 @@ module Style # # # good # format('%06d', 10) + # + # @example IgnoredMethods: [redirect] + # + # # good + # redirect('foo/%{bar_id}') + # class FormatStringToken < Base include ConfigurableEnforcedStyle + include IgnoredMethods def on_str(node) - return unless node.value.include?('%') - return if node.each_ancestor(:xstr, :regexp).any? + return if format_string_token?(node) || use_ignored_method?(node) detections = collect_detections(node) return if detections.empty? @@ -88,6 +96,14 @@ def on_str(node) } PATTERN + def format_string_token?(node) + !node.value.include?('%') || node.each_ancestor(:xstr, :regexp).any? + end + + def use_ignored_method?(node) + (parent = node.parent) && parent.send_type? && ignored_method?(parent.method_name) + end + def unannotated_format?(node, detected_style) detected_style == :unannotated && !format_string_in_typical_context?(node) end diff --git a/spec/rubocop/cop/style/format_string_token_spec.rb b/spec/rubocop/cop/style/format_string_token_spec.rb index bb008c8bb151..4eb5e8a569d1 100644 --- a/spec/rubocop/cop/style/format_string_token_spec.rb +++ b/spec/rubocop/cop/style/format_string_token_spec.rb @@ -2,12 +2,14 @@ RSpec.describe RuboCop::Cop::Style::FormatStringToken, :config do let(:enforced_style) { :annotated } + let(:ignored_methods) { [] } let(:cop_config) do { 'EnforcedStyle' => enforced_style, 'SupportedStyles' => %i[annotated template unannotated], - 'MaxUnannotatedPlaceholdersAllowed' => 0 + 'MaxUnannotatedPlaceholdersAllowed' => 0, + 'IgnoredMethods' => ignored_methods } end @@ -248,6 +250,27 @@ RUBY expect_no_corrections end + + context 'when `IgnoredMethods: redirect`' do + let(:ignored_methods) { ['redirect'] } + + it 'does not register an offense' do + expect_no_offenses(<<~RUBY) + redirect("%{foo}") + RUBY + end + end + + context 'when `IgnoredMethods: []`' do + let(:ignored_methods) { [] } + + it 'does not register an offense' do + expect_offense(<<~RUBY) + redirect("%{foo}") + ^^^^^^ Prefer annotated tokens (like `%s`) over template tokens (like `%{foo}`). + RUBY + end + end end context 'when enforced style is template' do