Skip to content

Commit ea8689b

Browse files
authored
Merge pull request #637 from aycabta/behavior-of-include-as-a-method
Behavior of "include" or "extend" as a method
2 parents 4c7e445 + 7655e65 commit ea8689b

File tree

2 files changed

+221
-15
lines changed

2 files changed

+221
-15
lines changed

lib/rdoc/parser/ruby.rb

+70-15
Original file line numberDiff line numberDiff line change
@@ -446,28 +446,83 @@ def get_constant
446446
end
447447

448448
##
449-
# Get a constant that may be surrounded by parens
449+
# Get an included module that may be surrounded by parens
450450

451-
def get_constant_with_optional_parens
451+
def get_included_module_with_optional_parens
452452
skip_tkspace false
453+
get_tkread
454+
tk = get_tk
455+
end_token = get_end_token tk
456+
return '' unless end_token
453457

454458
nest = 0
459+
continue = false
460+
only_constant = true
455461

456-
while :on_lparen == (tk = peek_tk)[:kind] do
457-
get_tk
458-
skip_tkspace
459-
nest += 1
460-
end
461-
462-
name = get_constant
463-
464-
while nest > 0
465-
skip_tkspace
462+
while tk != nil do
463+
is_element_of_constant = false
464+
case tk[:kind]
465+
when :on_semicolon then
466+
break if nest == 0
467+
when :on_lbracket then
468+
nest += 1
469+
when :on_rbracket then
470+
nest -= 1
471+
when :on_lbrace then
472+
nest += 1
473+
when :on_rbrace then
474+
nest -= 1
475+
if nest <= 0
476+
# we might have a.each { |i| yield i }
477+
unget_tk(tk) if nest < 0
478+
break
479+
end
480+
when :on_lparen then
481+
nest += 1
482+
when end_token[:kind] then
483+
if end_token[:kind] == :on_rparen
484+
nest -= 1
485+
break if nest <= 0
486+
else
487+
break if nest <= 0
488+
end
489+
when :on_rparen then
490+
nest -= 1
491+
when :on_comment, :on_embdoc then
492+
@read.pop
493+
if :on_nl == end_token[:kind] and "\n" == tk[:text][-1] and
494+
(!continue or (tk[:state] & RDoc::Parser::RipperStateLex::EXPR_LABEL) != 0) then
495+
break if !continue and nest <= 0
496+
end
497+
when :on_comma then
498+
continue = true
499+
when :on_ident then
500+
continue = false if continue
501+
when :on_kw then
502+
case tk[:text]
503+
when 'def', 'do', 'case', 'for', 'begin', 'class', 'module'
504+
nest += 1
505+
when 'if', 'unless', 'while', 'until', 'rescue'
506+
# postfix if/unless/while/until/rescue must be EXPR_LABEL
507+
nest += 1 unless (tk[:state] & RDoc::Parser::RipperStateLex::EXPR_LABEL) != 0
508+
when 'end'
509+
nest -= 1
510+
break if nest == 0
511+
end
512+
when :on_const then
513+
is_element_of_constant = true
514+
when :on_op then
515+
is_element_of_constant = true if '::' == tk[:text]
516+
end
517+
only_constant = false unless is_element_of_constant
466518
tk = get_tk
467-
nest -= 1 if :on_rparen == tk[:kind]
468519
end
469520

470-
name
521+
if only_constant
522+
get_tkread_clean(/\s+/, ' ')
523+
else
524+
''
525+
end
471526
end
472527

473528
##
@@ -1119,7 +1174,7 @@ def parse_extend_or_include klass, container, comment # :nodoc:
11191174
loop do
11201175
skip_tkspace_comment
11211176

1122-
name = get_constant_with_optional_parens
1177+
name = get_included_module_with_optional_parens
11231178

11241179
unless name.empty? then
11251180
obj = container.add klass, name, comment

test/test_rdoc_parser_ruby.rb

+151
Original file line numberDiff line numberDiff line change
@@ -4044,4 +4044,155 @@ class Defined
40444044
assert_equal ['A', 'B', 'B::C'], visible
40454045
end
40464046

4047+
def test_parse_include_by_dynamic_definition
4048+
util_parser <<-CLASS
4049+
module A
4050+
class B
4051+
include(Module.new do
4052+
def e(m)
4053+
end
4054+
end)
4055+
end
4056+
4057+
class C
4058+
end
4059+
4060+
class D
4061+
end
4062+
end
4063+
CLASS
4064+
4065+
@parser.scan
4066+
4067+
a = @store.find_module_named 'A'
4068+
assert_equal 'A', a.full_name
4069+
a_b = a.find_class_named 'B'
4070+
assert_equal 'A::B', a_b.full_name
4071+
a_c = a.find_class_named 'C'
4072+
assert_equal 'A::C', a_c.full_name
4073+
a_d = a.find_class_named 'D'
4074+
assert_equal 'A::D', a_d.full_name
4075+
end
4076+
4077+
def test_parse_include_by_dynamic_definition_without_paren
4078+
util_parser <<-CLASS
4079+
module A
4080+
class B
4081+
include(Module.new do
4082+
def e m
4083+
end
4084+
end)
4085+
end
4086+
4087+
class C
4088+
end
4089+
4090+
class D
4091+
end
4092+
end
4093+
CLASS
4094+
4095+
@parser.scan
4096+
4097+
a = @store.find_module_named 'A'
4098+
assert_equal 'A', a.full_name
4099+
a_b = a.find_class_named 'B'
4100+
assert_equal 'A::B', a_b.full_name
4101+
a_c = a.find_class_named 'C'
4102+
assert_equal 'A::C', a_c.full_name
4103+
a_d = a.find_class_named 'D'
4104+
assert_equal 'A::D', a_d.full_name
4105+
end
4106+
4107+
def test_parse_include_by_dynamic_definition_via_variable
4108+
util_parser <<-CLASS
4109+
module A
4110+
class B
4111+
m = Module.new do
4112+
def e(m)
4113+
end
4114+
end
4115+
include m
4116+
end
4117+
4118+
class C
4119+
end
4120+
4121+
class D
4122+
end
4123+
end
4124+
CLASS
4125+
4126+
@parser.scan
4127+
4128+
a = @store.find_module_named 'A'
4129+
assert_equal 'A', a.full_name
4130+
a_b = a.find_class_named 'B'
4131+
assert_equal 'A::B', a_b.full_name
4132+
a_c = a.find_class_named 'C'
4133+
assert_equal 'A::C', a_c.full_name
4134+
a_d = a.find_class_named 'D'
4135+
assert_equal 'A::D', a_d.full_name
4136+
end
4137+
4138+
def test_parse_include_by_dynamic_definition_with_brace
4139+
util_parser <<-CLASS
4140+
module A
4141+
class B
4142+
extend(e {
4143+
def f(g)
4144+
end
4145+
})
4146+
end
4147+
4148+
class C
4149+
end
4150+
4151+
class D
4152+
end
4153+
end
4154+
CLASS
4155+
4156+
@parser.scan
4157+
4158+
a = @store.find_module_named 'A'
4159+
assert_equal 'A', a.full_name
4160+
a_b = a.find_class_named 'B'
4161+
assert_equal 'A::B', a_b.full_name
4162+
a_c = a.find_class_named 'C'
4163+
assert_equal 'A::C', a_c.full_name
4164+
a_d = a.find_class_named 'D'
4165+
assert_equal 'A::D', a_d.full_name
4166+
end
4167+
4168+
def test_parse_include_by_dynamic_definition_directly
4169+
util_parser <<-CLASS
4170+
module A
4171+
class B
4172+
include Module.new do
4173+
def e m
4174+
end
4175+
end
4176+
end
4177+
4178+
class C
4179+
end
4180+
4181+
class D
4182+
end
4183+
end
4184+
CLASS
4185+
4186+
@parser.scan
4187+
4188+
a = @store.find_module_named 'A'
4189+
assert_equal 'A', a.full_name
4190+
a_b = a.find_class_named 'B'
4191+
assert_equal 'A::B', a_b.full_name
4192+
a_c = a.find_class_named 'C'
4193+
assert_equal 'A::C', a_c.full_name
4194+
a_d = a.find_class_named 'D'
4195+
assert_equal 'A::D', a_d.full_name
4196+
end
4197+
40474198
end

0 commit comments

Comments
 (0)