From 2bce39111a80cb7c4745bcf099d0d1c1f603840a Mon Sep 17 00:00:00 2001 From: st0012 Date: Sun, 22 Mar 2026 22:17:35 +0000 Subject: [PATCH] Fix :stopdoc: directive being undone for C-defined classes After parsing, `parse_file` resets `done_documenting = false` on all classes in `top_level.classes_or_modules`. The `done_documenting=` setter unconditionally sets `document_self = !value`, which overrides the `document_self = false` state set by `:stopdoc:`. This became visible after `add_to_classes_or_modules` was added to the C parser's `handle_class_module`, causing C-defined classes to appear in `top_level.classes_or_modules` for the first time. Fix by tracking when `:stopdoc:` is explicitly used via a `@stopped_doc` flag. The `done_documenting=` setter skips the `document_self` override when this flag is set. The flag is: - Set only when the `:stopdoc:` directive is processed - Cleared by `:startdoc:` - NOT set by `suppress` (which calls `stop_doc` internally but should remain reversible via `done_documenting = false`) --- lib/rdoc/code_object.rb | 11 ++++++++--- lib/rdoc/markup/pre_process.rb | 2 +- test/rdoc/parser/c_test.rb | 26 ++++++++++++++++++++++++++ 3 files changed, 35 insertions(+), 4 deletions(-) diff --git a/lib/rdoc/code_object.rb b/lib/rdoc/code_object.rb index c15c0129f5..fa4c55dfa0 100644 --- a/lib/rdoc/code_object.rb +++ b/lib/rdoc/code_object.rb @@ -125,6 +125,7 @@ def initialize_visibility # :nodoc: @received_nodoc = false @ignored = false @suppressed = false + @stopped_doc = false @track_visibility = true end @@ -205,8 +206,10 @@ def documented? def done_documenting=(value) return unless @track_visibility @done_documenting = value - @document_self = !value - @document_children = @document_self + unless @stopped_doc + @document_self = !value + @document_children = @document_self + end end ## @@ -343,16 +346,18 @@ def start_doc @document_children = true @ignored = false @suppressed = false + @stopped_doc = false end ## # Disable capture of documentation - def stop_doc + def stop_doc(from_directive: false) return unless @track_visibility @document_self = false @document_children = false + @stopped_doc = true if from_directive end ## diff --git a/lib/rdoc/markup/pre_process.rb b/lib/rdoc/markup/pre_process.rb index db42e36b1c..253a541054 100644 --- a/lib/rdoc/markup/pre_process.rb +++ b/lib/rdoc/markup/pre_process.rb @@ -231,7 +231,7 @@ def handle_directive(prefix, directive, param, code_object = nil, when 'stopdoc' then return blankline unless code_object - code_object.stop_doc + code_object.stop_doc(from_directive: true) blankline when 'yield', 'yields' then diff --git a/test/rdoc/parser/c_test.rb b/test/rdoc/parser/c_test.rb index 00b57d5d7d..81b6055561 100644 --- a/test/rdoc/parser/c_test.rb +++ b/test/rdoc/parser/c_test.rb @@ -2328,6 +2328,32 @@ def test_handle_method_source_file_with_non_ascii File.delete source_path if source_path && File.exist?(source_path) end + def test_stopdoc_class_display + content = <<~C + /* Document-class: Parent + * This is the Parent class + */ + VALUE rb_cParent = rb_define_class("Parent", rb_cObject); + + /* :stopdoc: */ + VALUE cInternal = rb_define_class_under(rb_cParent, "Internal", rb_cObject); + /* :startdoc: */ + C + + util_get_class content, 'cInternal' + + # Simulate parse_file's done_documenting reset + @top_level.classes_or_modules.each do |cm| + cm.done_documenting = false + end + + parent = @store.find_class_named 'Parent' + internal = @store.find_class_named 'Parent::Internal' + + assert parent.display?, 'Parent should be displayed' + refute internal.display?, 'stopdoc class should not be displayed' + end + def util_get_class(content, name = nil) @parser = util_parser content @parser.scan