Skip to content

Behavior of "include" or "extend" as a method #637

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 11, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 70 additions & 15 deletions lib/rdoc/parser/ruby.rb
Original file line number Diff line number Diff line change
Expand Up @@ -446,28 +446,83 @@ def get_constant
end

##
# Get a constant that may be surrounded by parens
# Get an included module that may be surrounded by parens

def get_constant_with_optional_parens
def get_included_module_with_optional_parens
skip_tkspace false
get_tkread
tk = get_tk
end_token = get_end_token tk
return '' unless end_token

nest = 0
continue = false
only_constant = true

while :on_lparen == (tk = peek_tk)[:kind] do
get_tk
skip_tkspace
nest += 1
end

name = get_constant

while nest > 0
skip_tkspace
while tk != nil do
is_element_of_constant = false
case tk[:kind]
when :on_semicolon then
break if nest == 0
when :on_lbracket then
nest += 1
when :on_rbracket then
nest -= 1
when :on_lbrace then
nest += 1
when :on_rbrace then
nest -= 1
if nest <= 0
# we might have a.each { |i| yield i }
unget_tk(tk) if nest < 0
break
end
when :on_lparen then
nest += 1
when end_token[:kind] then
if end_token[:kind] == :on_rparen
nest -= 1
break if nest <= 0
else
break if nest <= 0
end
when :on_rparen then
nest -= 1
when :on_comment, :on_embdoc then
@read.pop
if :on_nl == end_token[:kind] and "\n" == tk[:text][-1] and
(!continue or (tk[:state] & RDoc::Parser::RipperStateLex::EXPR_LABEL) != 0) then
break if !continue and nest <= 0
end
when :on_comma then
continue = true
when :on_ident then
continue = false if continue
when :on_kw then
case tk[:text]
when 'def', 'do', 'case', 'for', 'begin', 'class', 'module'
nest += 1
when 'if', 'unless', 'while', 'until', 'rescue'
# postfix if/unless/while/until/rescue must be EXPR_LABEL
nest += 1 unless (tk[:state] & RDoc::Parser::RipperStateLex::EXPR_LABEL) != 0
when 'end'
nest -= 1
break if nest == 0
end
when :on_const then
is_element_of_constant = true
when :on_op then
is_element_of_constant = true if '::' == tk[:text]
end
only_constant = false unless is_element_of_constant
tk = get_tk
nest -= 1 if :on_rparen == tk[:kind]
end

name
if only_constant
get_tkread_clean(/\s+/, ' ')
else
''
end
end

##
Expand Down Expand Up @@ -1119,7 +1174,7 @@ def parse_extend_or_include klass, container, comment # :nodoc:
loop do
skip_tkspace_comment

name = get_constant_with_optional_parens
name = get_included_module_with_optional_parens

unless name.empty? then
obj = container.add klass, name, comment
Expand Down
151 changes: 151 additions & 0 deletions test/test_rdoc_parser_ruby.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4044,4 +4044,155 @@ class Defined
assert_equal ['A', 'B', 'B::C'], visible
end

def test_parse_include_by_dynamic_definition
util_parser <<-CLASS
module A
class B
include(Module.new do
def e(m)
end
end)
end

class C
end

class D
end
end
CLASS

@parser.scan

a = @store.find_module_named 'A'
assert_equal 'A', a.full_name
a_b = a.find_class_named 'B'
assert_equal 'A::B', a_b.full_name
a_c = a.find_class_named 'C'
assert_equal 'A::C', a_c.full_name
a_d = a.find_class_named 'D'
assert_equal 'A::D', a_d.full_name
end

def test_parse_include_by_dynamic_definition_without_paren
util_parser <<-CLASS
module A
class B
include(Module.new do
def e m
end
end)
end

class C
end

class D
end
end
CLASS

@parser.scan

a = @store.find_module_named 'A'
assert_equal 'A', a.full_name
a_b = a.find_class_named 'B'
assert_equal 'A::B', a_b.full_name
a_c = a.find_class_named 'C'
assert_equal 'A::C', a_c.full_name
a_d = a.find_class_named 'D'
assert_equal 'A::D', a_d.full_name
end

def test_parse_include_by_dynamic_definition_via_variable
util_parser <<-CLASS
module A
class B
m = Module.new do
def e(m)
end
end
include m
end

class C
end

class D
end
end
CLASS

@parser.scan

a = @store.find_module_named 'A'
assert_equal 'A', a.full_name
a_b = a.find_class_named 'B'
assert_equal 'A::B', a_b.full_name
a_c = a.find_class_named 'C'
assert_equal 'A::C', a_c.full_name
a_d = a.find_class_named 'D'
assert_equal 'A::D', a_d.full_name
end

def test_parse_include_by_dynamic_definition_with_brace
util_parser <<-CLASS
module A
class B
extend(e {
def f(g)
end
})
end

class C
end

class D
end
end
CLASS

@parser.scan

a = @store.find_module_named 'A'
assert_equal 'A', a.full_name
a_b = a.find_class_named 'B'
assert_equal 'A::B', a_b.full_name
a_c = a.find_class_named 'C'
assert_equal 'A::C', a_c.full_name
a_d = a.find_class_named 'D'
assert_equal 'A::D', a_d.full_name
end

def test_parse_include_by_dynamic_definition_directly
util_parser <<-CLASS
module A
class B
include Module.new do
def e m
end
end
end

class C
end

class D
end
end
CLASS

@parser.scan

a = @store.find_module_named 'A'
assert_equal 'A', a.full_name
a_b = a.find_class_named 'B'
assert_equal 'A::B', a_b.full_name
a_c = a.find_class_named 'C'
assert_equal 'A::C', a_c.full_name
a_d = a.find_class_named 'D'
assert_equal 'A::D', a_d.full_name
end

end