From 8bee4c35380bb1fa5254fb4a0cc281a38ff87800 Mon Sep 17 00:00:00 2001
From: Quinton Miller <nicetas.c@gmail.com>
Date: Sat, 13 Feb 2021 09:13:00 +0800
Subject: [PATCH] Don't use key names for method_missing call's named params

---
 spec/compiler/codegen/method_missing_spec.cr  | 13 +++++++------
 .../crystal/semantic/method_missing.cr        | 19 +++++++++++--------
 2 files changed, 18 insertions(+), 14 deletions(-)

diff --git a/spec/compiler/codegen/method_missing_spec.cr b/spec/compiler/codegen/method_missing_spec.cr
index 35cba34610f4..721f32a5929d 100644
--- a/spec/compiler/codegen/method_missing_spec.cr
+++ b/spec/compiler/codegen/method_missing_spec.cr
@@ -382,11 +382,12 @@ describe "Code gen: method_missing" do
       )).to_string.should eq("bar")
   end
 
-  it "works with named arguments, using names (#3654)" do
+  it "works with named arguments (#3654)" do
     run(%(
       class A
         macro method_missing(call)
-          x &+ y
+          {{call.named_args[0].value}} &+
+            {{call.named_args[1].value}}
         end
       end
 
@@ -395,17 +396,17 @@ describe "Code gen: method_missing" do
       )).to_i.should eq(3)
   end
 
-  it "works with named arguments, named args in call (#3654)" do
+  it "works with named arguments that aren't legal variable names (#10381)" do
     run(%(
       class A
         macro method_missing(call)
-          {{call.named_args[0].name}} &+
-            {{call.named_args[1].name}}
+          {{call.named_args[0].value}} &+
+            {{call.named_args[1].value}}
         end
       end
 
       a = A.new
-      a.b(x: 1, y: 2)
+      a.b("@x": 1, Y: 2)
       )).to_i.should eq(3)
   end
 
diff --git a/src/compiler/crystal/semantic/method_missing.cr b/src/compiler/crystal/semantic/method_missing.cr
index ceb95571a82e..5b108e73befb 100644
--- a/src/compiler/crystal/semantic/method_missing.cr
+++ b/src/compiler/crystal/semantic/method_missing.cr
@@ -31,18 +31,19 @@ module Crystal
       name_node = StringLiteral.new(signature.name)
       args_nodes = [] of ASTNode
       named_args_nodes = nil
-      args_nodes_names = Set(String).new
+      args_nodes_names = [] of {String?, String} # external <-> internal name
       signature.arg_types.each_index do |index|
         arg_node_name = "_arg#{index}"
         args_nodes << MacroId.new(arg_node_name)
-        args_nodes_names << arg_node_name
+        args_nodes_names << {nil, arg_node_name}
       end
       if named_args = signature.named_args
-        args_nodes_names << ""
-        named_args.each do |named_arg|
+        args_nodes_names << {nil, ""}
+        named_args.each_with_index do |named_arg, index|
+          named_arg_node_name = "_named_arg#{index}"
           named_args_nodes ||= [] of NamedArgument
-          named_args_nodes << NamedArgument.new(named_arg.name, MacroId.new(named_arg.name))
-          args_nodes_names << named_arg.name
+          named_args_nodes << NamedArgument.new(named_arg.name, MacroId.new(named_arg_node_name))
+          args_nodes_names << {named_arg.name, named_arg_node_name}
         end
       end
       if block = signature.block
@@ -56,7 +57,7 @@ module Crystal
         block_node = Nop.new
       end
 
-      a_def = Def.new(signature.name, args_nodes_names.map { |name| Arg.new(name) })
+      a_def = Def.new(signature.name, args_nodes_names.map { |ext_name, name| Arg.new(name, external_name: ext_name) })
       a_def.splat_index = signature.arg_types.size if signature.named_args
 
       call = Call.new(nil, signature.name,
@@ -70,9 +71,11 @@ module Crystal
       # Check if the expanded macro is a def. We do this
       # by just lexing the result and seeing if the first
       # token is `def`
+      macro_vars = Set(String).new
+      args_nodes_names.each { |_, name| macro_vars << name }
       expands_to_def = starts_with_def?(expanded_macro)
       generated_nodes =
-        program.parse_macro_source(expanded_macro, macro_expansion_pragmas, method_missing, method_missing, args_nodes_names) do |parser|
+        program.parse_macro_source(expanded_macro, macro_expansion_pragmas, method_missing, method_missing, macro_vars) do |parser|
           if expands_to_def
             parser.parse
           else