Skip to content

Regression: Parse methods inside ActiveSupport::Concern included blocks#1753

Closed
skatkov wants to merge 3 commits into
ruby:masterfrom
skatkov:parse-concern-methods
Closed

Regression: Parse methods inside ActiveSupport::Concern included blocks#1753
skatkov wants to merge 3 commits into
ruby:masterfrom
skatkov:parse-concern-methods

Conversation

@skatkov

@skatkov skatkov commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Issue

When generating documentation for rails, i'm seeing a following error:

ActiveRecord/ConnectionHandling.html:
`rdoc-ref:Core.configurations` can't be resolved for ``

RDoc does not add ActiveRecord::Core.configurations to its object store. Resulting documentation renders label as plain text instead of linking to the method documentation.

Context

Rails defines some documented methods inside concern inclusion blocks:

module ActiveRecord
  module Core
    extend ActiveSupport::Concern

    included do
      # Returns a fully resolved ActiveRecord::DatabaseConfigurations object.
      def self.configurations
        @@configurations
      end
    end
  end
end

Previously this block could be parsed, support for ActiveSupport::Concern#included was originally added in #819. During the migration to the Prism parser and subsequent removal of the Ripper parser, this special handling was not carried over.

Solution

Handle special-case receiverless included calls that have a block.

Their block body is visited directly, without marking it as a generic proc block. Calls without a block continue through the normal visitor path.

Ordinary metaprogramming blocks remain unchanged and method definitions inside them are still ignored.

Follow-up

@zzak voiced a valid question:

#819 (comment)

This deserves a follow-up PR's.

  • prepended is analogous and could be a small follow-up using when :included, :prepended.
  • class_methods needs separate handling.

Copilot AI review requested due to automatic review settings July 22, 2026 14:47
@skatkov
skatkov requested a deployment to fork-preview-protection July 22, 2026 14:47 — with GitHub Actions Waiting

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes a regression in the Prism-based Ruby parser where method definitions inside ActiveSupport::Concern receiverless included do ... end blocks were no longer visited, causing documented Rails methods (e.g., ActiveRecord::Core.configurations) to be missing from the object store and cross-reference resolution to fail.

Changes:

  • Special-case receiverless included calls with a block so the block body is visited directly (without entering the generic “proc/metaprogramming block” mode that ignores method defs).
  • Add a focused regression test ensuring def self.configurations inside an included block is stored and its doc comment is preserved.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.

File Description
lib/rdoc/parser/ruby.rb Visits receiverless included block bodies directly so method definitions inside are parsed and recorded.
test/rdoc/parser/ruby_test.rb Adds a regression test asserting methods and comments inside ActiveSupport::Concern included blocks are captured.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copilot AI review requested due to automatic review settings July 22, 2026 15:30
@skatkov
skatkov requested a deployment to fork-preview-protection July 22, 2026 15:31 — with GitHub Actions Waiting

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

Comment thread lib/rdoc/parser/ruby.rb
Comment on lines +895 to +896
when :included
node.block ? node.block.body&.accept(self) : super
Comment on lines +759 to +760
assert_equal ['A::foo', 'A#bar', 'A::configurations', 'A::baz2', 'A#baz3'], methods.map(&:full_name)
assert_equal ['comment foo', 'comment bar', 'Returns the configuration.'], methods.take(3).map { |m| m.comment.text.strip }
@tompng

tompng commented Jul 22, 2026

Copy link
Copy Markdown
Member

Thanks for the PR, but I don't think we should take this approach.

First, on the premise: this behavior didn't come from #819. The pre-Prism parser documented def inside any block (Struct.new do ... end, RSpec helper blocks, ...), which produced many wrong documents. The Prism-based parser intentionally dropped that — see the comment in visit_block_node, and the existing test this PR modifies, which asserts that def inside a block is ignored.

Second, included is not Ruby semantics — it's ActiveSupport::Concern's. In plain Ruby, class A; included do end; end raises ArgumentError (Module#included is the core inclusion hook, arity 1), and other libraries can define included with different block semantics. The parser deliberately interprets only Ruby's own semantics (attr_accessor, include, visibility, ...), and interpreting a method name by one library's spec doesn't fit there. If RDoc ever supports this, it should be an explicitly scoped extension (e.g. activated by detecting extend ActiveSupport::Concern, covering class_methods do as well) — but that's a separate discussion.

As a workaround, # :singleton-method: / # :method: directives work inside blocks today and can cover the ActiveRecord::Core case.

@skatkov skatkov closed this Jul 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants