From 956d5a11d34d78393c404752e78997f1a1931c47 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Thu, 31 Mar 2016 14:22:40 -0300 Subject: [PATCH] Expand macro expressions arguments before macro calls. Fixes #2388 --- spec/compiler/codegen/macro_spec.cr | 22 ++++++++++ .../crystal/semantic/base_type_visitor.cr | 41 +++++++++++++++++-- 2 files changed, 60 insertions(+), 3 deletions(-) diff --git a/spec/compiler/codegen/macro_spec.cr b/spec/compiler/codegen/macro_spec.cr index e1c439635b36..5e66b75c3433 100644 --- a/spec/compiler/codegen/macro_spec.cr +++ b/spec/compiler/codegen/macro_spec.cr @@ -1269,4 +1269,26 @@ describe "Code gen: macro" do id(A) )).to_i.should eq(1) end + + it "solves macro expression arguments before macro expansion (type)" do + run(%( + macro name(x) + {{x.name.stringify}} + end + + name({{String}}) + )).to_string.should eq("String") + end + + it "solves macro expression arguments before macro expansion (constant)" do + run(%( + CONST = 1 + + macro id(x) + {{x}} + end + + id({{CONST}}) + )).to_i.should eq(1) + end end diff --git a/src/compiler/crystal/semantic/base_type_visitor.cr b/src/compiler/crystal/semantic/base_type_visitor.cr index 59c69366a5c2..fec631b9c7a6 100644 --- a/src/compiler/crystal/semantic/base_type_visitor.cr +++ b/src/compiler/crystal/semantic/base_type_visitor.cr @@ -496,12 +496,18 @@ module Crystal node.raise "macro '#{node.name}' must be defined before this point but is defined later" end - @exp_nest -= 1 + expansion_scope = (macro_scope || @scope || current_type) + + args = expand_macro_arguments(node, expansion_scope) + @exp_nest -= 1 generated_nodes = expand_macro(the_macro, node) do - @mod.expand_macro the_macro, node, (macro_scope || @scope || current_type) + old_args = node.args + node.args = args + expanded = @mod.expand_macro the_macro, node, expansion_scope + node.args = old_args + expanded end - @exp_nest += 1 node.expanded = generated_nodes @@ -531,6 +537,35 @@ module Crystal generated_nodes end + def expand_macro_arguments(node, expansion_scope) + # If any argument is a MacroExpression, solve it first and + # replace Path with Const/TypeNode if it denotes such thing + args = node.args + if args.any? &.is_a?(MacroExpression) + @exp_nest -= 1 + args = args.map do |arg| + if arg.is_a?(MacroExpression) + arg.accept self + expanded = arg.expanded.not_nil! + if expanded.is_a?(Path) + expanded_type = expansion_scope.lookup_type(expanded) + case expanded_type + when Const + expanded = expanded_type.value + when Type + expanded = TypeNode.new(expanded_type) + end + end + expanded + else + arg + end + end + @exp_nest += 1 + end + args + end + def visit(node : MacroExpression) expand_inline_macro node end