Skip to content
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

Fix crystal tool expand for private definitions #3998

Merged
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
129 changes: 101 additions & 28 deletions spec/compiler/crystal/tools/expand_spec.cr
Expand Up @@ -340,7 +340,7 @@ describe "expand" do
assert_expand_simple code, original: "foo", expanded: ":Foo\n:foo\n"
end

%w(module class struct lib enum).each do |keyword|
%w(module class struct enum lib).each do |keyword|
it "expands macro expression inside #{keyword}" do
code = <<-CODE
#{keyword} Foo
Expand All @@ -350,6 +350,33 @@ describe "expand" do

assert_expand_simple code, original: %({{ "Foo = 1".id }}), expanded: "Foo = 1"
end

it "expands macro expression inside private #{keyword}" do
code = <<-CODE
private #{keyword} Foo
‸{{ "Foo = 1".id }}
end
CODE

assert_expand_simple code, original: %({{ "Foo = 1".id }}), expanded: "Foo = 1"
end

unless keyword == "lib"
it "expands macro expression inside def of private #{keyword}" do
code = <<-CODE
private #{keyword} Foo
Foo = 1
def self.foo
{{ :‸foo }}
end
end

Foo.foo
CODE

assert_expand_simple code, original: "{{ :foo }}", expanded: ":foo"
end
end
end

%w(struct union).each do |keyword|
Expand All @@ -364,42 +391,88 @@ describe "expand" do

assert_expand_simple code, original: %({{ "Foo = 1".id }}), expanded: "Foo = 1"
end

it "expands macro expression inside C #{keyword} of private lib" do
code = <<-CODE
private lib Foo
#{keyword} Foo
‸{{ "Foo = 1".id }}
end
end
CODE

assert_expand_simple code, original: %({{ "Foo = 1".id }}), expanded: "Foo = 1"
end
end

it "expands macro expression inside def" do
code = <<-CODE
def foo(x : T) forall T
‸{{ T }}
["", "private "].each do |prefix|
it "expands macro expression inside #{prefix}def" do
code = <<-CODE
#{prefix}def foo(x : T) forall T
‸{{ T }}
end

foo 1
foo "bar"
CODE

assert_expand code, [
["{{ T }}", "Int32"],
["{{ T }}", "String"],
]
end

foo 1
foo "bar"
CODE
it "expands macro expression inside def of #{prefix}module" do
code = <<-CODE
#{prefix}module Foo(T)
def self.foo
{{ ‸T }}
end
end

assert_expand code, [
["{{ T }}", "Int32"],
["{{ T }}", "String"],
]
end
Foo(Int32).foo
Foo(String).foo
Foo(1).foo
CODE

it "expands macro expression inside def of module" do
code = <<-CODE
module Foo(T)
def self.foo
{{ ‸T }}
assert_expand code, [
["{{ T }}", "Int32"],
["{{ T }}", "String"],
["{{ T }}", "1"],
]
end

it "expands macro expression inside def of nested #{prefix}module" do
code = <<-CODE
#{prefix}module Foo
#{prefix}module Bar(T)
def self.foo
{{ ‸T }}
end
end

Bar(Int32).foo
Bar(String).foo
Bar(1).foo
end
CODE

assert_expand code, [
["{{ T }}", "Int32"],
["{{ T }}", "String"],
["{{ T }}", "1"],
]
end
end

Foo(Int32).foo
Foo(String).foo
Foo(1).foo
it "expands macro expression inside fun" do
code = <<-CODE
fun foo
{{ :foo‸ }}
end
CODE

assert_expand code, [
["{{ T }}", "Int32"],
["{{ T }}", "String"],
["{{ T }}", "1"],
]
assert_expand_simple code, original: "{{ :foo }}", expanded: ":foo"
end

it "doesn't expand macro expression" do
Expand Down Expand Up @@ -436,7 +509,7 @@ describe "expand" do
‸foo
CODE

assert_expand_fail code, "no expansion found: foo is not macro"
assert_expand_fail code, "no expansion found: foo may not be a macro"
end

it "expands macro with doc" do
Expand All @@ -448,7 +521,7 @@ describe "expand" do
end
# symbol of {{ x }}
def {{ x }}_sym
:{{ x }}
{{ x.symbolize }}
end
end

Expand Down
3 changes: 2 additions & 1 deletion src/compiler/crystal/syntax/parser.cr
Expand Up @@ -4536,11 +4536,12 @@ module Crystal

def parse_visibility_modifier(modifier)
doc = @token.doc
location = @token.location

next_token_skip_space
exp = parse_op_assign

modifier = VisibilityModifier.new(modifier, exp)
modifier = VisibilityModifier.new(modifier, exp).at(location).at_end(exp)
modifier.doc = doc
exp.doc = doc
modifier
Expand Down
23 changes: 8 additions & 15 deletions src/compiler/crystal/tools/expand.cr
Expand Up @@ -77,20 +77,18 @@ module Crystal
end

def process_type(type)
if type.is_a?(NamedType)
type.types?.try &.values.each do |inner_type|
return unless type

if type.is_a?(NamedType) || type.is_a?(Program) || type.is_a?(FileModule)
type.types?.try &.each_value do |inner_type|
process_type(inner_type)
end
end

process_type type.metaclass if type.metaclass != type

if type.is_a?(DefInstanceContainer)
type.def_instances.values.try do |typed_defs|
typed_defs.each do |typed_def|
typed_def.accept(self)
end
end
type.def_instances.each_value &.accept(self)
end

if type.is_a?(GenericType)
Expand All @@ -102,13 +100,8 @@ module Crystal

def process(result : Compiler::Result)
@in_defs = true
result.program.def_instances.each_value do |typed_def|
typed_def.accept(self)
end

result.program.types?.try &.values.each do |type|
process_type type
end
process_type result.program
process_type result.program.file_module?(@target_location.original_filename)
@in_defs = false

result.node.accept(self)
Expand Down Expand Up @@ -136,7 +129,7 @@ module Crystal
if node.expanded
@found_nodes << node
else
@message = "no expansion found: #{node} is not macro"
@message = "no expansion found: #{node} may not be a macro"
end
false
else
Expand Down