diff --git a/CHANGELOG.md b/CHANGELOG.md index 77a6745f1a..f572f1650c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ * [#436](https://github.com/rubocop/rubocop-rails/issues/436): Fix a false positive for `Rails/ContentTag` when the first argument is a splat argument. ([@koic][]) * [#435](https://github.com/rubocop/rubocop-rails/issues/435): Fix a false negative for `Rails/BelongsTo` when using `belongs_to` lambda block with `required` option. ([@koic][]) * [#451](https://github.com/rubocop/rubocop-rails/issues/451): Fix a false negative for `Rails/RelativeDateConstant` when a method is chained after a relative date method. ([@koic][]) +* [#450](https://github.com/rubocop/rubocop-rails/issues/450): Fix a crash for `Rails/ContentTag` with nested content tags. ([@tejasbubane][]) ### Changes diff --git a/lib/rubocop/cop/rails/content_tag.rb b/lib/rubocop/cop/rails/content_tag.rb index 9a8ad3aeb5..47f519ffee 100644 --- a/lib/rubocop/cop/rails/content_tag.rb +++ b/lib/rubocop/cop/rails/content_tag.rb @@ -28,17 +28,30 @@ class ContentTag < Base MSG = 'Use `tag` instead of `content_tag`.' RESTRICT_ON_SEND = %i[content_tag].freeze + def on_new_investigation + @corrected_nodes = nil + end + def on_send(node) first_argument = node.first_argument - return if !first_argument || allowed_argument?(first_argument) + return if !first_argument || + allowed_argument?(first_argument) || + corrected_ancestor?(node) add_offense(node) do |corrector| autocorrect(corrector, node) + + @corrected_nodes ||= Set.new.compare_by_identity + @corrected_nodes.add(node) end end private + def corrected_ancestor?(node) + node.each_ancestor(:send).any? { |ancestor| @corrected_nodes&.include?(ancestor) } + end + def allowed_argument?(argument) argument.variable? || argument.send_type? || argument.const_type? || argument.splat_type? end diff --git a/spec/rubocop/cop/rails/content_tag_spec.rb b/spec/rubocop/cop/rails/content_tag_spec.rb index bbb9f52eb7..f2b80945fd 100644 --- a/spec/rubocop/cop/rails/content_tag_spec.rb +++ b/spec/rubocop/cop/rails/content_tag_spec.rb @@ -23,6 +23,8 @@ it 'does not register an offense with nested content_tag' do expect_no_offenses(<<~RUBY) content_tag(:div) { content_tag(:strong, 'Hi') } + + content_tag(:div, content_tag(:span, 'foo')) RUBY end end @@ -62,6 +64,17 @@ end it 'corrects an offence with nested content_tag' do + expect_offense(<<~RUBY) + content_tag(:div, content_tag(:span, 'foo')) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Use `tag` instead of `content_tag`. + RUBY + + expect_correction(<<~RUBY) + tag.div(tag.span('foo')) + RUBY + end + + it 'corrects an offence with nested content_tag with block' do expect_offense(<<~RUBY) content_tag(:div) { content_tag(:strong, 'Hi') } ^^^^^^^^^^^^^^^^^^^^^^^^^^ Use `tag` instead of `content_tag`.