GN: Use the correct defaults for templates invoked via target().

Previously calling
  target("foo", "bar")
where "foo" is a template applied the defaults for the function named "target"
(since this is the function call) rather than "foo".

This change properly applies the defaults for "foo" in this case.

BUG=624564

Review-Url: https://codereview.chromium.org/2148093002
Cr-Original-Commit-Position: refs/heads/master@{#405547}
Cr-Mirrored-From: https://chromium.googlesource.com/chromium/src
Cr-Mirrored-Commit: af92718f5526ce94ea8fcb4118f850fb9a7517b3
diff --git a/tools/gn/functions.cc b/tools/gn/functions.cc
index 0a00c9c..0102bd7 100644
--- a/tools/gn/functions.cc
+++ b/tools/gn/functions.cc
@@ -1036,13 +1036,14 @@
       function_map.find(name.value());
   if (found_function == function_map.end()) {
     // No built-in function matching this, check for a template.
-    const Template* templ =
-        scope->GetTemplate(function->function().value().as_string());
+    std::string template_name = function->function().value().as_string();
+    const Template* templ = scope->GetTemplate(template_name);
     if (templ) {
       Value args = args_list->Execute(scope, err);
       if (err->has_error())
         return Value();
-      return templ->Invoke(scope, function, args.list_value(), block, err);
+      return templ->Invoke(scope, function, template_name, args.list_value(),
+                           block, err);
     }
 
     *err = Err(name, "Unknown function.");
diff --git a/tools/gn/functions_target.cc b/tools/gn/functions_target.cc
index 4e162d5..8aa7f93 100644
--- a/tools/gn/functions_target.cc
+++ b/tools/gn/functions_target.cc
@@ -773,7 +773,7 @@
   // Run a template if it is one.
   const Template* templ = scope->GetTemplate(target_type);
   if (templ)
-    return templ->Invoke(scope, function, sub_args, block, err);
+    return templ->Invoke(scope, function, target_type, sub_args, block, err);
 
   // Otherwise, assume the target is a built-in target type.
   return ExecuteGenericTarget(target_type.c_str(), scope, function, sub_args,
diff --git a/tools/gn/functions_target_unittest.cc b/tools/gn/functions_target_unittest.cc
index a7a0de9..f4eeb38 100644
--- a/tools/gn/functions_target_unittest.cc
+++ b/tools/gn/functions_target_unittest.cc
@@ -36,3 +36,37 @@
   source_set_input.parsed()->Execute(setup.scope(), &err);
   ASSERT_TRUE(err.has_error());
 }
+
+// Checks that the defaults applied to a template invoked by target() use
+// the name of the template, rather than the string "target" (which is the
+// name of the actual function being called).
+TEST(FunctionsTarget, TemplateDefaults) {
+  Scheduler scheduler;
+  TestWithScope setup;
+
+  // The target generator needs a place to put the targets or it will fail.
+  Scope::ItemVector item_collector;
+  setup.scope()->set_item_collector(&item_collector);
+
+  // Test a good one first.
+  TestParseInput good_input(
+      // Make a template with defaults set.
+      "template(\"my_templ\") {\n"
+      "  source_set(target_name) {\n"
+      "    forward_variables_from(invoker, \"*\")\n"
+      "  }\n"
+      "}\n"
+      "set_defaults(\"my_templ\") {\n"
+      "  default_value = 1\n"
+      "}\n"
+
+      // Invoke the template with target(). This will fail to execute if the
+      // defaults were not set properly, because "default_value" won't exist.
+      "target(\"my_templ\", \"foo\") {\n"
+      "  print(default_value)\n"
+      "}\n");
+  ASSERT_FALSE(good_input.has_error());
+  Err err;
+  good_input.parsed()->Execute(setup.scope(), &err);
+  ASSERT_FALSE(err.has_error()) << err.message();
+}
diff --git a/tools/gn/template.cc b/tools/gn/template.cc
index 8b8ae69..3c4d76d 100644
--- a/tools/gn/template.cc
+++ b/tools/gn/template.cc
@@ -26,6 +26,7 @@
 
 Value Template::Invoke(Scope* scope,
                        const FunctionCallNode* invocation,
+                       const std::string& template_name,
                        const std::vector<Value>& args,
                        BlockNode* block,
                        Err* err) const {
@@ -37,8 +38,7 @@
   // First run the invocation's block. Need to allocate the scope on the heap
   // so we can pass ownership to the template.
   std::unique_ptr<Scope> invocation_scope(new Scope(scope));
-  if (!FillTargetBlockScope(scope, invocation,
-                            invocation->function().value().as_string(),
+  if (!FillTargetBlockScope(scope, invocation, template_name,
                             block, args, invocation_scope.get(), err))
     return Value();
 
diff --git a/tools/gn/template.h b/tools/gn/template.h
index a79d82f..da5a1a4 100644
--- a/tools/gn/template.h
+++ b/tools/gn/template.h
@@ -33,9 +33,12 @@
   Template(std::unique_ptr<Scope> closure, const FunctionCallNode* def);
 
   // Invoke the template. The values correspond to the state of the code
-  // invoking the template.
+  // invoking the template. The template name needs to be supplied since the
+  // template object itself doesn't know what name the calling code is using
+  // to refer to it (this is used to set defaults).
   Value Invoke(Scope* scope,
                const FunctionCallNode* invocation,
+               const std::string& template_name,
                const std::vector<Value>& args,
                BlockNode* block,
                Err* err) const;