Automated rollback of commit 7c2174fea2b50af8fbf10367835b820af29700f6.

*** Reason for rollback ***

Breaks the nightly

*** Original change description ***

bazel syntax: delete BuiltinFunction.Factory

The mechanism existed so that functions like glob could
be closures over the PackageContext, which is now accessible
as a thread-local value.

Also:
- replace FuncallExpression with Location in various calls
  All of these are for EvalException and could thus be deleted.
- document element types for StarlarkList<?> parameters
PiperOrigin-RevId: 276738614
diff --git a/src/main/java/com/google/devtools/build/lib/packages/PackageFactory.java b/src/main/java/com/google/devtools/build/lib/packages/PackageFactory.java
index 89d4f1e..82bcc56 100644
--- a/src/main/java/com/google/devtools/build/lib/packages/PackageFactory.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/PackageFactory.java
@@ -540,43 +540,47 @@
                     + " excluded).")
       },
       documented = false,
-      useLocation = true,
+      useAst = true,
       useStarlarkThread = true)
-  private static final BuiltinFunction globFunction =
-      new BuiltinFunction("glob") {
-        public SkylarkList<String> invoke(
-            SkylarkList<?> include, // <String>
-            SkylarkList<?> exclude, // <String>
-            Integer excludeDirectories,
-            Object allowEmpty,
-            Location loc,
-            StarlarkThread thread)
-            throws EvalException, InterruptedException {
-          return callGlob(
-              getContext(thread, loc),
-              include,
-              exclude,
-              excludeDirectories != 0,
-              allowEmpty,
-              loc,
-              thread);
+  private static final BuiltinFunction.Factory newGlobFunction =
+      new BuiltinFunction.Factory("glob") {
+        public BuiltinFunction create(final PackageContext originalContext) {
+          return new BuiltinFunction("glob", this) {
+            public SkylarkList invoke(
+                SkylarkList include,
+                SkylarkList exclude,
+                Integer excludeDirectories,
+                Object allowEmpty,
+                FuncallExpression ast,
+                StarlarkThread thread)
+                throws EvalException, ConversionException, InterruptedException {
+              return callGlob(
+                  originalContext,
+                  include,
+                  exclude,
+                  excludeDirectories != 0,
+                  allowEmpty,
+                  ast,
+                  thread);
+            }
+          };
         }
       };
 
-  static SkylarkList<String> callGlob(
+  static SkylarkList<Object> callGlob(
       @Nullable PackageContext originalContext,
-      SkylarkList<?> include, // <String>
-      SkylarkList<?> exclude, // <String>
+      Object include,
+      Object exclude,
       boolean excludeDirs,
       Object allowEmptyArgument,
-      Location loc,
+      FuncallExpression ast,
       StarlarkThread thread)
       throws EvalException, ConversionException, InterruptedException {
     // Skylark build extensions need to get the PackageContext from the StarlarkThread;
     // async glob functions cannot do the same because the StarlarkThread is not thread safe.
     PackageContext context;
     if (originalContext == null) {
-      context = getContext(thread, loc);
+      context = getContext(thread, ast.getLocation());
     } else {
       context = originalContext;
     }
@@ -592,7 +596,8 @@
       allowEmpty = (Boolean) allowEmptyArgument;
     } else {
       throw new EvalException(
-          loc, "expected boolean for argument `allow_empty`, got `" + allowEmptyArgument + "`");
+          ast.getLocation(),
+          "expected boolean for argument `allow_empty`, got `" + allowEmptyArgument + "`");
     }
 
     try {
@@ -605,11 +610,11 @@
           Joiner.on(", ").join(includes),
           excludes.isEmpty() ? "" : " - [" + Joiner.on(", ").join(excludes) + "]",
           e.getMessage());
-      context.eventHandler.handle(Event.error(loc, errorMessage));
+      context.eventHandler.handle(Event.error(ast.getLocation(), errorMessage));
       context.pkgBuilder.setIOExceptionAndMessage(e, errorMessage);
       matches = ImmutableList.of();
     } catch (BadGlobException e) {
-      throw new EvalException(loc, e.getMessage());
+      throw new EvalException(ast.getLocation(), e.getMessage());
     }
 
     return MutableList.copyOf(thread, matches);
@@ -633,22 +638,32 @@
         @Param(name = "name", type = String.class, doc = "The name of the target."),
       },
       documented = false,
-      useLocation = true,
+      useAst = true,
       useStarlarkThread = true)
-  private static final BuiltinFunction existingRuleFunction =
-      new BuiltinFunction("existing_rule") {
-        public Object invoke(String name, Location loc, StarlarkThread thread)
-            throws EvalException {
-          return callExistingRule(name, loc, thread);
+  private static final BuiltinFunction.Factory newExistingRuleFunction =
+      new BuiltinFunction.Factory("existing_rule") {
+        public BuiltinFunction create(final PackageContext originalContext) {
+          return new BuiltinFunction("existing_rule", this) {
+            public Object invoke(String name, FuncallExpression ast, StarlarkThread thread)
+                throws EvalException {
+              return callExistingRule(name, ast, thread);
+            }
+          };
         }
       };
 
-  static Object callExistingRule(String name, Location loc, StarlarkThread thread)
+  static Object callExistingRule(String name, FuncallExpression ast, StarlarkThread thread)
       throws EvalException {
-    PackageContext context = getContext(thread, loc);
+
+    PackageContext context = getContext(thread, ast.getLocation());
     Target target = context.pkgBuilder.getTarget(name);
-    SkylarkDict<String, Object> rule = targetDict(target, loc, thread);
-    return rule != null ? rule : Runtime.NONE;
+    SkylarkDict<String, Object> rule = targetDict(target, ast.getLocation(), thread);
+
+    if (rule != null) {
+      return rule;
+    }
+
+    return Runtime.NONE;
   }
 
   /**
@@ -657,7 +672,6 @@
    */
   @SkylarkSignature(
       name = "existing_rules",
-      returnType = SkylarkDict.class,
       doc =
           "Returns a dictionary containing all the targets instantiated so far. The map key is the "
               + "name of the target. The map value is equivalent to the <code>existing_rule</code> "
@@ -665,20 +679,26 @@
               + ""
               + "<p><i>Note: If possible, avoid using this function. It makes BUILD files brittle "
               + "and order-dependent.</i>",
-      useLocation = true,
+      useAst = true,
       useStarlarkThread = true)
-  private static final BuiltinFunction existingRulesFunction =
-      new BuiltinFunction("existing_rules") {
-        public SkylarkDict<String, SkylarkDict<String, Object>> invoke(
-            Location loc, StarlarkThread thread) throws EvalException {
-          return callExistingRules(loc, thread);
+  private static final BuiltinFunction.Factory newExistingRulesFunction =
+      new BuiltinFunction.Factory("existing_rules") {
+        public BuiltinFunction create(final PackageContext originalContext) {
+          return new BuiltinFunction("existing_rules", this) {
+            public SkylarkDict<String, SkylarkDict<String, Object>> invoke(
+                FuncallExpression ast, StarlarkThread thread) throws EvalException {
+              return callExistingRules(ast, thread);
+            }
+          };
         }
       };
 
   static SkylarkDict<String, SkylarkDict<String, Object>> callExistingRules(
-      Location loc, StarlarkThread thread) throws EvalException {
-    PackageContext context = getContext(thread, loc);
+      FuncallExpression ast, StarlarkThread thread) throws EvalException {
+    PackageContext context = getContext(thread, ast.getLocation());
     Collection<Target> targets = context.pkgBuilder.getTargets();
+    Location loc = ast.getLocation();
+
     SkylarkDict<String, SkylarkDict<String, Object>> rules = SkylarkDict.of(thread);
     for (Target t : targets) {
       if (t instanceof Rule) {
@@ -692,8 +712,8 @@
   }
 
   /**
-   * Function value implementing "environment_group" in the specified package context. Syntax is as
-   * follows:
+   * Returns a function value implementing "environment_group" in the specified package context.
+   * Syntax is as follows:
    *
    * <pre>{@code
    * environment_group(
@@ -736,43 +756,42 @@
             doc = "A list of Labels.")
       }, // TODO(bazel-team): document what that is
       documented = false,
-      useLocation = true,
-      useStarlarkThread = true)
-  private static final BuiltinFunction environmentGroupFunction =
-      new BuiltinFunction("environment_group") {
-        public Runtime.NoneType invoke(
-            String name,
-            SkylarkList<?> environmentsList, // <Label>
-            SkylarkList<?> defaultsList, // <Label>
-            Location loc,
-            StarlarkThread thread)
-            throws EvalException {
-          PackageContext context = getContext(thread, loc);
-          List<Label> environments =
-              BuildType.LABEL_LIST.convert(
-                  environmentsList,
-                  "'environment_group argument'",
-                  context.pkgBuilder.getBuildFileLabel());
-          List<Label> defaults =
-              BuildType.LABEL_LIST.convert(
-                  defaultsList,
-                  "'environment_group argument'",
-                  context.pkgBuilder.getBuildFileLabel());
+      useLocation = true)
+  private static final BuiltinFunction.Factory newEnvironmentGroupFunction =
+      new BuiltinFunction.Factory("environment_group") {
+        public BuiltinFunction create(final PackageContext context) {
+          return new BuiltinFunction("environment_group", this) {
+            public Runtime.NoneType invoke(
+                String name, SkylarkList environmentsList, SkylarkList defaultsList, Location loc)
+                throws EvalException, ConversionException {
+              List<Label> environments =
+                  BuildType.LABEL_LIST.convert(
+                      environmentsList,
+                      "'environment_group argument'",
+                      context.pkgBuilder.getBuildFileLabel());
+              List<Label> defaults =
+                  BuildType.LABEL_LIST.convert(
+                      defaultsList,
+                      "'environment_group argument'",
+                      context.pkgBuilder.getBuildFileLabel());
 
-          if (environments.isEmpty()) {
-            throw new EvalException(
-                location, "environment group " + name + " must contain at least one environment");
-          }
-          try {
-            context.pkgBuilder.addEnvironmentGroup(
-                name, environments, defaults, context.eventHandler, loc);
-            return Runtime.NONE;
-          } catch (LabelSyntaxException e) {
-            throw new EvalException(
-                loc, "environment group has invalid name: " + name + ": " + e.getMessage());
-          } catch (Package.NameConflictException e) {
-            throw new EvalException(loc, e.getMessage());
-          }
+              if (environments.isEmpty()) {
+                throw new EvalException(
+                    location,
+                    "environment group " + name + " must contain at least one environment");
+              }
+              try {
+                context.pkgBuilder.addEnvironmentGroup(
+                    name, environments, defaults, context.eventHandler, loc);
+                return Runtime.NONE;
+              } catch (LabelSyntaxException e) {
+                throw new EvalException(
+                    loc, "environment group has invalid name: " + name + ": " + e.getMessage());
+              } catch (Package.NameConflictException e) {
+                throw new EvalException(loc, e.getMessage());
+              }
+            }
+          };
         }
       };
 
@@ -806,25 +825,33 @@
             doc = "A list of strings specifying the licenses used in the exported code.")
       },
       documented = false,
-      useLocation = true,
+      useAst = true,
       useStarlarkThread = true)
-  private static final BuiltinFunction exportsFilesFunction =
-      new BuiltinFunction("exports_files") {
-        public Runtime.NoneType invoke(
-            SkylarkList<?> srcs, // <String>
-            Object visibility,
-            Object licenses,
-            Location loc,
-            StarlarkThread thread)
-            throws EvalException {
-          return callExportsFiles(srcs, visibility, licenses, loc, thread);
+  private static final BuiltinFunction.Factory newExportsFilesFunction =
+      new BuiltinFunction.Factory("exports_files") {
+        public BuiltinFunction create() {
+          return new BuiltinFunction("exports_files", this) {
+            public Runtime.NoneType invoke(
+                SkylarkList srcs,
+                Object visibility,
+                Object licenses,
+                FuncallExpression ast,
+                StarlarkThread thread)
+                throws EvalException, ConversionException {
+              return callExportsFiles(srcs, visibility, licenses, ast, thread);
+            }
+          };
         }
       };
 
   static Runtime.NoneType callExportsFiles(
-      Object srcs, Object visibilityO, Object licensesO, Location loc, StarlarkThread thread)
+      Object srcs,
+      Object visibilityO,
+      Object licensesO,
+      FuncallExpression ast,
+      StarlarkThread thread)
       throws EvalException, ConversionException {
-    Package.Builder pkgBuilder = getContext(thread, loc).pkgBuilder;
+    Package.Builder pkgBuilder = getContext(thread, ast.getLocation()).pkgBuilder;
     List<String> files = Type.STRING_LIST.convert(srcs, "'exports_files' operand");
 
     RuleVisibility visibility;
@@ -836,7 +863,7 @@
               "'exports_files' operand",
               pkgBuilder.getBuildFileLabel()));
     } catch (EvalException e) {
-      throw new EvalException(loc, e.getMessage());
+      throw new EvalException(ast.getLocation(), e.getMessage());
     }
     // TODO(bazel-team): is licenses plural or singular?
     License license = BuildType.LICENSE.convertOptional(licensesO, "'exports_files' operand");
@@ -844,21 +871,20 @@
     for (String file : files) {
       String errorMessage = LabelValidator.validateTargetName(file);
       if (errorMessage != null) {
-        throw new EvalException(loc, errorMessage);
+        throw new EvalException(ast.getLocation(), errorMessage);
       }
       try {
-        InputFile inputFile = pkgBuilder.createInputFile(file, loc);
+        InputFile inputFile = pkgBuilder.createInputFile(file, ast.getLocation());
         if (inputFile.isVisibilitySpecified()
             && inputFile.getVisibility() != visibility) {
-          throw new EvalException(
-              loc,
-              String.format(
-                  "visibility for exported file '%s' declared twice", inputFile.getName()));
+          throw new EvalException(ast.getLocation(),
+              String.format("visibility for exported file '%s' declared twice",
+                  inputFile.getName()));
         }
         if (license != null && inputFile.isLicenseSpecified()) {
-          throw new EvalException(
-              loc,
-              String.format("licenses for exported file '%s' declared twice", inputFile.getName()));
+          throw new EvalException(ast.getLocation(),
+              String.format("licenses for exported file '%s' declared twice",
+                  inputFile.getName()));
         }
 
         // See if we should check third-party licenses: first checking for any hard-coded policy,
@@ -878,63 +904,54 @@
             && license == null
             && !pkgBuilder.getDefaultLicense().isSpecified()
             && RuleClass.isThirdPartyPackage(pkgBuilder.getPackageIdentifier())) {
-          throw new EvalException(
-              loc,
-              "third-party file '"
-                  + inputFile.getName()
-                  + "' lacks a license declaration "
-                  + "with one of the following types: notice, reciprocal, permissive, "
-                  + "restricted, unencumbered, by_exception_only");
+          throw new EvalException(ast.getLocation(),
+              "third-party file '" + inputFile.getName() + "' lacks a license declaration "
+              + "with one of the following types: notice, reciprocal, permissive, "
+              + "restricted, unencumbered, by_exception_only");
         }
 
         pkgBuilder.setVisibilityAndLicense(inputFile, visibility, license);
       } catch (Package.Builder.GeneratedLabelConflict e) {
-        throw new EvalException(loc, e.getMessage());
+        throw new EvalException(ast.getLocation(), e.getMessage());
       }
     }
     return Runtime.NONE;
   }
 
   /**
-   * Returns a function-value implementing "licenses" in the specified package context.
+   * Returns a function-value implementing "licenses" in the specified package
+   * context.
    * TODO(bazel-team): Remove in favor of package.licenses.
    */
-  @SkylarkSignature(
-      name = "licenses",
-      returnType = Runtime.NoneType.class,
+  @SkylarkSignature(name = "licenses", returnType = Runtime.NoneType.class,
       doc = "Declare the license(s) for the code in the current package.",
       parameters = {
-        @Param(
-            name = "license_strings",
-            type = SkylarkList.class,
-            generic1 = String.class,
-            doc = "A list of strings, the names of the licenses used.")
-      },
-      documented = false,
-      useStarlarkThread = true,
-      useLocation = true)
-  private static final BuiltinFunction licensesFunction =
-      new BuiltinFunction("licenses") {
-        public Runtime.NoneType invoke(
-            SkylarkList<?> licensesList, // list of license strings
-            Location loc,
-            StarlarkThread thread)
-            throws EvalException {
-          PackageContext context = getContext(thread, loc);
-          try {
-            License license = BuildType.LICENSE.convert(licensesList, "'licenses' operand");
-            context.pkgBuilder.setDefaultLicense(license);
-          } catch (ConversionException e) {
-            context.eventHandler.handle(Event.error(loc, e.getMessage()));
-            context.pkgBuilder.setContainsErrors();
-          }
-          return Runtime.NONE;
+        @Param(name = "license_strings", type = SkylarkList.class, generic1 = String.class,
+            doc = "A list of strings, the names of the licenses used.")},
+      documented = false, useLocation = true)
+  private static final BuiltinFunction.Factory newLicensesFunction =
+      new BuiltinFunction.Factory("licenses") {
+        public BuiltinFunction create(final PackageContext context) {
+          return new BuiltinFunction("licenses", this) {
+            public Runtime.NoneType invoke(SkylarkList licensesList, Location loc) {
+              try {
+                License license = BuildType.LICENSE.convert(licensesList, "'licenses' operand");
+                context.pkgBuilder.setDefaultLicense(license);
+              } catch (ConversionException e) {
+                context.eventHandler.handle(Event.error(loc, e.getMessage()));
+                context.pkgBuilder.setContainsErrors();
+              }
+              return Runtime.NONE;
+            }
+          };
         }
       };
 
   /** Returns a function-value implementing "distribs" in the specified package context. */
   // TODO(bazel-team): Remove in favor of package.distribs.
-  // TODO(bazel-team): share functions with the native package. Requires unifying the List types.
+  // TODO(bazel-team): Remove all these new*Function-s and/or have static functions
+  // that consult the context dynamically via getContext(thread, loc) since we have that,
+  // and share the functions with the native package... which requires unifying the List types.
   @SkylarkSignature(
       name = "distribs",
       returnType = Runtime.NoneType.class,
@@ -943,23 +960,23 @@
         @Param(name = "distribution_strings", type = Object.class, doc = "The distributions.")
       },
       documented = false,
-      useStarlarkThread = true,
       useLocation = true)
-  private static final BuiltinFunction distribsFunction =
-      new BuiltinFunction("distribs") {
-        public Runtime.NoneType invoke(Object object, Location loc, StarlarkThread thread)
-            throws EvalException {
-          PackageContext context = getContext(thread, loc);
-
-          try {
-            Set<DistributionType> distribs =
-                BuildType.DISTRIBUTIONS.convert(object, "'distribs' operand");
-            context.pkgBuilder.setDefaultDistribs(distribs);
-          } catch (ConversionException e) {
-            context.eventHandler.handle(Event.error(loc, e.getMessage()));
-            context.pkgBuilder.setContainsErrors();
-          }
-          return Runtime.NONE;
+  private static final BuiltinFunction.Factory newDistribsFunction =
+      new BuiltinFunction.Factory("distribs") {
+        public BuiltinFunction create(final PackageContext context) {
+          return new BuiltinFunction("distribs", this) {
+            public Runtime.NoneType invoke(Object object, Location loc) {
+              try {
+                Set<DistributionType> distribs =
+                    BuildType.DISTRIBUTIONS.convert(object, "'distribs' operand");
+                context.pkgBuilder.setDefaultDistribs(distribs);
+              } catch (ConversionException e) {
+                context.eventHandler.handle(Event.error(loc, e.getMessage()));
+                context.pkgBuilder.setContainsErrors();
+              }
+              return Runtime.NONE;
+            }
+          };
         }
       };
 
@@ -993,18 +1010,22 @@
             doc = "A list of Label specifiers for the files to include.")
       },
       documented = false,
-      useLocation = true,
+      useAst = true,
       useStarlarkThread = true)
-  private static final BuiltinFunction packageGroupFunction =
-      new BuiltinFunction("package_group") {
-        public Runtime.NoneType invoke(
-            String name,
-            SkylarkList<?> packages, // <String>
-            SkylarkList<?> includes, // <Label>
-            Location loc,
-            StarlarkThread thread)
-            throws EvalException {
-          return callPackageFunction(name, packages, includes, loc, thread);
+  private static final BuiltinFunction.Factory newPackageGroupFunction =
+      new BuiltinFunction.Factory("package_group") {
+        public BuiltinFunction create() {
+          return new BuiltinFunction("package_group", this) {
+            public Runtime.NoneType invoke(
+                String name,
+                SkylarkList packages,
+                SkylarkList includes,
+                FuncallExpression ast,
+                StarlarkThread thread)
+                throws EvalException, ConversionException {
+              return callPackageFunction(name, packages, includes, ast, thread);
+            }
+          };
         }
       };
 
@@ -1165,9 +1186,9 @@
   }
 
   static Runtime.NoneType callPackageFunction(
-      String name, Object packagesO, Object includesO, Location loc, StarlarkThread thread)
+      String name, Object packagesO, Object includesO, FuncallExpression ast, StarlarkThread thread)
       throws EvalException, ConversionException {
-    PackageContext context = getContext(thread, loc);
+    PackageContext context = getContext(thread, ast.getLocation());
 
     List<String> packages = Type.STRING_LIST.convert(
         packagesO, "'package_group.packages argument'");
@@ -1175,13 +1196,14 @@
         "'package_group.includes argument'", context.pkgBuilder.getBuildFileLabel());
 
     try {
-      context.pkgBuilder.addPackageGroup(name, packages, includes, context.eventHandler, loc);
+      context.pkgBuilder.addPackageGroup(name, packages, includes, context.eventHandler,
+          ast.getLocation());
       return Runtime.NONE;
     } catch (LabelSyntaxException e) {
-      throw new EvalException(
-          loc, "package group has invalid name: " + name + ": " + e.getMessage());
+      throw new EvalException(ast.getLocation(),
+          "package group has invalid name: " + name + ": " + e.getMessage());
     } catch (Package.NameConflictException e) {
-      throw new EvalException(loc, e.getMessage());
+      throw new EvalException(ast.getLocation(), e.getMessage());
     }
   }
 
@@ -1221,13 +1243,13 @@
       @Override
       public Object call(Object[] arguments, FuncallExpression ast, StarlarkThread thread)
           throws EvalException {
-        Location loc = ast.getLocation();
 
-        Package.Builder pkgBuilder = getContext(thread, loc).pkgBuilder;
+        Package.Builder pkgBuilder = getContext(thread, ast.getLocation()).pkgBuilder;
 
         // Validate parameter list
         if (pkgBuilder.isPackageFunctionUsed()) {
-          throw new EvalException(loc, "'package' can only be used once per BUILD file");
+          throw new EvalException(
+              ast.getLocation(), "'package' can only be used once per BUILD file");
         }
         pkgBuilder.setPackageFunctionUsed();
 
@@ -1238,13 +1260,13 @@
           Object value = arguments[i];
           if (value != null) {
             foundParameter = true;
-            argumentSpecifiers[i].convertAndProcess(pkgBuilder, loc, value);
+            argumentSpecifiers[i].convertAndProcess(pkgBuilder, ast.getLocation(), value);
           }
         }
 
         if (!foundParameter) {
           throw new EvalException(
-              loc, "at least one argument must be given to the 'package' function");
+              ast.getLocation(), "at least one argument must be given to the 'package' function");
         }
 
         return Runtime.NONE;
@@ -1618,7 +1640,8 @@
     return StructProvider.STRUCT.create(builder.build(), "no native function or rule '%s'");
   }
 
-  private void populateEnvironment(ImmutableMap.Builder<String, Object> env) {
+  private void populateEnvironment(
+      ImmutableMap.Builder<String, Object> env, PackageContext context) {
     // TODO(bazel-team): remove the naked functions that are redundant with the nativeModule,
     // or if not possible, at least make them straight copies from the native module variant.
     // or better, use a common StarlarkThread.Frame for these common bindings
@@ -1636,17 +1659,17 @@
     }
 
     env.putAll(BazelLibrary.GLOBALS.getBindings());
-    env.put("distribs", distribsFunction);
-    env.put("glob", globFunction);
-    env.put("licenses", licensesFunction);
-    env.put("exports_files", exportsFilesFunction);
-    env.put("package_group", packageGroupFunction);
+    env.put("distribs", newDistribsFunction.apply(context));
+    env.put("glob", newGlobFunction.apply(context));
+    env.put("licenses", newLicensesFunction.apply(context));
+    env.put("exports_files", newExportsFilesFunction.apply());
+    env.put("package_group", newPackageGroupFunction.apply());
     env.put("package", newPackageFunction(packageArguments));
     env.put("package_name", packageNameFunction);
     env.put("repository_name", repositoryNameFunction);
-    env.put("environment_group", environmentGroupFunction);
-    env.put("existing_rule", existingRuleFunction);
-    env.put("existing_rules", existingRulesFunction);
+    env.put("environment_group", newEnvironmentGroupFunction.apply(context));
+    env.put("existing_rule", newExistingRuleFunction.apply(context));
+    env.put("existing_rules", newExistingRulesFunction.apply(context));
     env.putAll(ruleFunctions);
 
     for (EnvironmentExtension ext : environmentExtensions) {
@@ -1707,9 +1730,12 @@
         .setWorkspaceName(workspaceName)
         .setRepositoryMapping(repositoryMapping);
 
+    // Stuff that closes over the package context:
+    PackageContext context = new PackageContext(pkgBuilder, globber, eventHandler);
+
     // environment
     ImmutableMap.Builder<String, Object> env = ImmutableMap.builder();
-    populateEnvironment(env);
+    populateEnvironment(env, context);
 
     try (Mutability mutability = Mutability.create("package %s", packageId)) {
       StarlarkThread thread =
@@ -1723,8 +1749,7 @@
 
       // TODO(adonovan): save this as a field in BazelSkylarkContext.
       // It needn't be a third thread-local.
-      thread.setThreadLocal(
-          PackageContext.class, new PackageContext(pkgBuilder, globber, eventHandler));
+      thread.setThreadLocal(PackageContext.class, context);
 
       new BazelStarlarkContext(
               ruleClassProvider.getToolsRepository(),
diff --git a/src/main/java/com/google/devtools/build/lib/packages/SkylarkNativeModule.java b/src/main/java/com/google/devtools/build/lib/packages/SkylarkNativeModule.java
index ad5d8f4..f818957 100644
--- a/src/main/java/com/google/devtools/build/lib/packages/SkylarkNativeModule.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/SkylarkNativeModule.java
@@ -19,28 +19,17 @@
 import com.google.devtools.build.lib.packages.Type.ConversionException;
 import com.google.devtools.build.lib.skylarkbuildapi.SkylarkNativeModuleApi;
 import com.google.devtools.build.lib.syntax.EvalException;
+import com.google.devtools.build.lib.syntax.FuncallExpression;
 import com.google.devtools.build.lib.syntax.Runtime;
 import com.google.devtools.build.lib.syntax.SkylarkDict;
 import com.google.devtools.build.lib.syntax.SkylarkList;
 import com.google.devtools.build.lib.syntax.SkylarkUtils;
 import com.google.devtools.build.lib.syntax.StarlarkThread;
 
-/** The Skylark native module. */
-// TODO(laurentlb): Some definitions are duplicated from PackageFactory.
-// This class defines:
-// native.existing_rule
-// native.existing_rules
-// native.exports_files -- also global
-// native.glob          -- also global
-// native.package_group -- also global
-// native.package_name
-// native.repository_name
-//
-// PackageFactory also defines:
-// distribs            -- hidden?
-// licenses            -- hidden?
-// package             -- global
-// environment_group   -- hidden?
+/**
+ * A class for the Skylark native module. TODO(laurentlb): Some definitions are duplicated from
+ * PackageFactory.
+ */
 public class SkylarkNativeModule implements SkylarkNativeModuleApi {
 
   @Override
@@ -49,23 +38,23 @@
       SkylarkList<?> exclude,
       Integer excludeDirectories,
       Object allowEmpty,
-      Location loc,
+      FuncallExpression ast,
       StarlarkThread thread)
       throws EvalException, ConversionException, InterruptedException {
-    SkylarkUtils.checkLoadingPhase(thread, "native.glob", loc);
+    SkylarkUtils.checkLoadingPhase(thread, "native.glob", ast.getLocation());
     try {
       return PackageFactory.callGlob(
-          null, include, exclude, excludeDirectories != 0, allowEmpty, loc, thread);
+          null, include, exclude, excludeDirectories != 0, allowEmpty, ast, thread);
     } catch (IllegalArgumentException e) {
-      throw new EvalException(loc, "illegal argument in call to glob", e);
+      throw new EvalException(ast.getLocation(), "illegal argument in call to glob", e);
     }
   }
 
   @Override
-  public Object existingRule(String name, Location loc, StarlarkThread thread)
+  public Object existingRule(String name, FuncallExpression ast, StarlarkThread thread)
       throws EvalException, InterruptedException {
-    SkylarkUtils.checkLoadingOrWorkspacePhase(thread, "native.existing_rule", loc);
-    return PackageFactory.callExistingRule(name, loc, thread);
+    SkylarkUtils.checkLoadingOrWorkspacePhase(thread, "native.existing_rule", ast.getLocation());
+    return PackageFactory.callExistingRule(name, ast, thread);
   }
 
   /*
@@ -74,9 +63,9 @@
   */
   @Override
   public SkylarkDict<String, SkylarkDict<String, Object>> existingRules(
-      Location loc, StarlarkThread thread) throws EvalException, InterruptedException {
-    SkylarkUtils.checkLoadingOrWorkspacePhase(thread, "native.existing_rules", loc);
-    return PackageFactory.callExistingRules(loc, thread);
+      FuncallExpression ast, StarlarkThread thread) throws EvalException, InterruptedException {
+    SkylarkUtils.checkLoadingOrWorkspacePhase(thread, "native.existing_rules", ast.getLocation());
+    return PackageFactory.callExistingRules(ast, thread);
   }
 
   @Override
@@ -84,26 +73,30 @@
       String name,
       SkylarkList<?> packages,
       SkylarkList<?> includes,
-      Location loc,
+      FuncallExpression ast,
       StarlarkThread thread)
       throws EvalException {
-    SkylarkUtils.checkLoadingPhase(thread, "native.package_group", loc);
-    return PackageFactory.callPackageFunction(name, packages, includes, loc, thread);
+    SkylarkUtils.checkLoadingPhase(thread, "native.package_group", ast.getLocation());
+    return PackageFactory.callPackageFunction(name, packages, includes, ast, thread);
   }
 
   @Override
   public Runtime.NoneType exportsFiles(
-      SkylarkList<?> srcs, Object visibility, Object licenses, Location loc, StarlarkThread thread)
+      SkylarkList<?> srcs,
+      Object visibility,
+      Object licenses,
+      FuncallExpression ast,
+      StarlarkThread thread)
       throws EvalException {
-    SkylarkUtils.checkLoadingPhase(thread, "native.exports_files", loc);
-    return PackageFactory.callExportsFiles(srcs, visibility, licenses, loc, thread);
+    SkylarkUtils.checkLoadingPhase(thread, "native.exports_files", ast.getLocation());
+    return PackageFactory.callExportsFiles(srcs, visibility, licenses, ast, thread);
   }
 
   @Override
-  public String packageName(Location loc, StarlarkThread thread) throws EvalException {
-    SkylarkUtils.checkLoadingPhase(thread, "native.package_name", loc);
+  public String packageName(FuncallExpression ast, StarlarkThread thread) throws EvalException {
+    SkylarkUtils.checkLoadingPhase(thread, "native.package_name", ast.getLocation());
     PackageIdentifier packageId =
-        PackageFactory.getContext(thread, loc).getBuilder().getPackageIdentifier();
+        PackageFactory.getContext(thread, ast.getLocation()).getBuilder().getPackageIdentifier();
     return packageId.getPackageFragment().getPathString();
   }
 
diff --git a/src/main/java/com/google/devtools/build/lib/skylarkbuildapi/SkylarkNativeModuleApi.java b/src/main/java/com/google/devtools/build/lib/skylarkbuildapi/SkylarkNativeModuleApi.java
index 228b838..c5e6091 100644
--- a/src/main/java/com/google/devtools/build/lib/skylarkbuildapi/SkylarkNativeModuleApi.java
+++ b/src/main/java/com/google/devtools/build/lib/skylarkbuildapi/SkylarkNativeModuleApi.java
@@ -20,6 +20,7 @@
 import com.google.devtools.build.lib.skylarkinterface.SkylarkModule;
 import com.google.devtools.build.lib.skylarkinterface.SkylarkModuleCategory;
 import com.google.devtools.build.lib.syntax.EvalException;
+import com.google.devtools.build.lib.syntax.FuncallExpression;
 import com.google.devtools.build.lib.syntax.Runtime;
 import com.google.devtools.build.lib.syntax.SkylarkDict;
 import com.google.devtools.build.lib.syntax.SkylarkList;
@@ -85,14 +86,14 @@
                     + " result must be non-empty (after the matches of the `exclude` patterns are"
                     + " excluded).")
       },
-      useLocation = true,
+      useAst = true,
       useStarlarkThread = true)
   public SkylarkList<?> glob(
       SkylarkList<?> include,
       SkylarkList<?> exclude,
       Integer excludeDirectories,
       Object allowEmpty,
-      Location loc,
+      FuncallExpression ast,
       StarlarkThread thread)
       throws EvalException, InterruptedException;
 
@@ -112,9 +113,9 @@
             legacyNamed = true,
             doc = "The name of the target.")
       },
-      useLocation = true,
+      useAst = true,
       useStarlarkThread = true)
-  public Object existingRule(String name, Location loc, StarlarkThread thread)
+  public Object existingRule(String name, FuncallExpression ast, StarlarkThread thread)
       throws EvalException, InterruptedException;
 
   @SkylarkCallable(
@@ -126,10 +127,10 @@
               + ""
               + "<p><i>Note: If possible, avoid using this function. It makes BUILD files brittle "
               + "and order-dependent.</i>",
-      useLocation = true,
+      useAst = true,
       useStarlarkThread = true)
   public SkylarkDict<String, SkylarkDict<String, Object>> existingRules(
-      Location loc, StarlarkThread thread) throws EvalException, InterruptedException;
+      FuncallExpression ast, StarlarkThread thread) throws EvalException, InterruptedException;
 
   @SkylarkCallable(
       name = "package_group",
@@ -160,13 +161,13 @@
             positional = false,
             doc = "Other package groups that are included in this one.")
       },
-      useLocation = true,
+      useAst = true,
       useStarlarkThread = true)
   public Runtime.NoneType packageGroup(
       String name,
       SkylarkList<?> packages,
       SkylarkList<?> includes,
-      Location loc,
+      FuncallExpression ast,
       StarlarkThread thread)
       throws EvalException;
 
@@ -202,10 +203,14 @@
             defaultValue = "None",
             doc = "Licenses to be specified.")
       },
-      useLocation = true,
+      useAst = true,
       useStarlarkThread = true)
   public Runtime.NoneType exportsFiles(
-      SkylarkList<?> srcs, Object visibility, Object licenses, Location loc, StarlarkThread thread)
+      SkylarkList<?> srcs,
+      Object visibility,
+      Object licenses,
+      FuncallExpression ast,
+      StarlarkThread thread)
       throws EvalException;
 
   @SkylarkCallable(
@@ -218,9 +223,9 @@
               + "<code>package_name()</code> will match the caller BUILD file package. "
               + "This function is equivalent to the deprecated variable <code>PACKAGE_NAME</code>.",
       parameters = {},
-      useLocation = true,
+      useAst = true,
       useStarlarkThread = true)
-  public String packageName(Location loc, StarlarkThread thread) throws EvalException;
+  public String packageName(FuncallExpression ast, StarlarkThread thread) throws EvalException;
 
   @SkylarkCallable(
       name = "repository_name",
diff --git a/src/main/java/com/google/devtools/build/lib/skylarkinterface/SkylarkSignature.java b/src/main/java/com/google/devtools/build/lib/skylarkinterface/SkylarkSignature.java
index 5942b68..e0fbeaa2 100644
--- a/src/main/java/com/google/devtools/build/lib/skylarkinterface/SkylarkSignature.java
+++ b/src/main/java/com/google/devtools/build/lib/skylarkinterface/SkylarkSignature.java
@@ -21,10 +21,10 @@
 /**
  * An annotation to mark built-in keyword argument methods accessible from Skylark.
  *
- * <p>Use this annotation around a {@link com.google.devtools.build.lib.syntax.BuiltinFunction}. The
- * annotated function should expect the arguments described by {@link #parameters()}, {@link
- * #extraPositionals()}, and {@link #extraKeywords()}. It should also expect the following
- * extraneous arguments:
+ * <p>Use this annotation around a {@link com.google.devtools.build.lib.syntax.BuiltinFunction} or a
+ * {@link com.google.devtools.build.lib.syntax.BuiltinFunction.Factory}. The annotated function
+ * should expect the arguments described by {@link #parameters()}, {@link #extraPositionals()}, and
+ * {@link #extraKeywords()}. It should also expect the following extraneous arguments:
  *
  * <ul>
  *   <li>{@link com.google.devtools.build.lib.events.Location} if {@link #useLocation()} is true.
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/BaseFunction.java b/src/main/java/com/google/devtools/build/lib/syntax/BaseFunction.java
index 6f139ae..af8d758 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/BaseFunction.java
+++ b/src/main/java/com/google/devtools/build/lib/syntax/BaseFunction.java
@@ -55,7 +55,7 @@
   // TODO(adonovan): this class has too many fields and relies too heavily on side effects and the
   // class hierarchy (the configure methods are the worse offenders). Turn fields into abstract
   // methods. Make processArguments a static function with multiple parameters, instead of a
-  // "mix-in" that accesses instance fields.
+  // "mix-in" that accesses instance fields. And get rid of BuiltinFunction.Factory.
 
   /**
    * The name of the function.
@@ -92,9 +92,12 @@
   // Some functions are also Namespaces or other Skylark entities.
   @Nullable protected Class<?> objectType;
 
+  // Documentation for variables, if any
+  @Nullable protected List<String> paramDoc;
+
   // The types actually enforced by the Skylark runtime, as opposed to those enforced by the JVM,
   // or those displayed to the user in the documentation.
-  @Nullable List<SkylarkType> enforcedArgumentTypes;
+  @Nullable protected List<SkylarkType> enforcedArgumentTypes;
 
   /**
    * Returns the name of this function.
@@ -176,6 +179,11 @@
     this(name, signature, /*defaultValues=*/ null, /*location=*/ null);
   }
 
+  /** Get parameter documentation as a list corresponding to each parameter */
+  List<String> getParamDoc() {
+    return paramDoc;
+  }
+
   /**
    * The size of the array required by the callee.
    */
@@ -488,10 +496,12 @@
   public void configure(SkylarkSignature annotation) {
     Preconditions.checkState(!isConfigured()); // must not be configured yet
 
+    this.paramDoc = new ArrayList<>();
+
     // side effect: appends to getEnforcedArgumentTypes()
     SkylarkSignatureProcessor.SignatureInfo info =
         SkylarkSignatureProcessor.getSignatureForCallable(
-            getName(), annotation, /*paramDoc=*/ new ArrayList<>(), getEnforcedArgumentTypes());
+            getName(), annotation, paramDoc, getEnforcedArgumentTypes());
     this.signature = info.signature;
     this.paramTypes = info.types;
     this.defaultValues = info.defaultValues;
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/BuiltinFunction.java b/src/main/java/com/google/devtools/build/lib/syntax/BuiltinFunction.java
index 652bc24..af176ba 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/BuiltinFunction.java
+++ b/src/main/java/com/google/devtools/build/lib/syntax/BuiltinFunction.java
@@ -86,11 +86,21 @@
     configure();
   }
 
+  /** Creates a BuiltinFunction from the given name and a Factory */
+  protected BuiltinFunction(String name, Factory factory) {
+    super(name);
+    configure(factory);
+  }
+
   @Override
   protected int getArgArraySize() {
     return innerArgumentCount;
   }
 
+  ExtraArgKind[] getExtraArgs() {
+    return extraArgs;
+  }
+
   @Override
   @Nullable
   public Object call(Object[] args, @Nullable FuncallExpression ast, StarlarkThread thread)
@@ -188,7 +198,7 @@
             stacktraceToString(e.getStackTrace()),
             this,
             Arrays.asList(invokeMethod.getParameterTypes()),
-            getEnforcedArgumentTypes()),
+            enforcedArgumentTypes),
         e);
   }
 
@@ -234,9 +244,7 @@
                   parameterType);
           if (enforcedType instanceof SkylarkType.Simple
               || enforcedType instanceof SkylarkFunctionType) {
-            Preconditions.checkArgument(
-                parameterType.isAssignableFrom(enforcedType.getType()), msg);
-
+            Preconditions.checkArgument(enforcedType.getType() == parameterType, msg);
             // No need to enforce Simple types on the Skylark side, the JVM will do it for us.
             enforcedArgumentTypes.set(i, null);
           } else if (enforcedType instanceof SkylarkType.Combination) {
@@ -263,6 +271,24 @@
     }
   }
 
+  /** Configure by copying another function's configuration */
+  // Alternatively, we could have an extension BuiltinFunctionSignature of FunctionSignature,
+  // and use *that* instead of a Factory.
+  private void configure(BuiltinFunction.Factory factory) {
+    // this function must not be configured yet, but the factory must be
+    Preconditions.checkState(!isConfigured());
+    Preconditions.checkState(
+        factory.isConfigured(), "function factory is not configured for %s", getName());
+
+    this.paramDoc = factory.getParamDoc();
+    this.signature = factory.getSignature();
+    this.defaultValues = factory.getDefaultValues();
+    this.paramTypes = factory.getEnforcedArgumentTypes();
+    this.extraArgs = factory.getExtraArgs();
+    this.objectType = factory.getObjectType();
+    configure();
+  }
+
   /** Returns list, or null if all its elements are null. */
   @Nullable
   private static <E> List<E> valueListOrNull(List<E> list) {
@@ -296,6 +322,50 @@
     return found;
   }
 
+  /**
+   * A Factory allows for a @SkylarkSignature annotation to be provided and processed in advance
+   * for a function that will be defined later as a closure (see e.g. in PackageFactory).
+   *
+   * <p>Each instance of this class must define a method create that closes over some (final)
+   * variables and returns a BuiltinFunction.
+   */
+  public abstract static class Factory extends BuiltinFunction {
+    @Nullable private Method createMethod;
+
+    /** Create unconfigured function Factory from its name */
+    public Factory(String name) {
+      super(name);
+    }
+
+    @Override
+    public void configure() {
+      if (createMethod != null) {
+        return;
+      }
+      createMethod = findMethod("create");
+    }
+
+    @Override
+    public Object call(Object[] args, @Nullable FuncallExpression ast, StarlarkThread thread)
+        throws EvalException {
+      throw new EvalException(null, "tried to invoke a Factory for function " + this);
+    }
+
+    /** Instantiate the Factory
+     * @param args arguments to pass to the create method
+     * @return a new BuiltinFunction that closes over the arguments
+     */
+    public BuiltinFunction apply(Object... args) {
+      try {
+        return (BuiltinFunction) createMethod.invoke(this, args);
+      } catch (InvocationTargetException | IllegalArgumentException | IllegalAccessException e) {
+        throw new RuntimeException(String.format(
+            "Exception while applying BuiltinFunction.Factory %s: %s",
+            this, e.getMessage()), e);
+      }
+    }
+  }
+
   @Override
   public void repr(SkylarkPrinter printer) {
     printer.append("<built-in function " + getName() + ">");
diff --git a/src/main/java/com/google/devtools/build/skydoc/fakebuildapi/FakeSkylarkNativeModuleApi.java b/src/main/java/com/google/devtools/build/skydoc/fakebuildapi/FakeSkylarkNativeModuleApi.java
index 0928823..4376ac4 100644
--- a/src/main/java/com/google/devtools/build/skydoc/fakebuildapi/FakeSkylarkNativeModuleApi.java
+++ b/src/main/java/com/google/devtools/build/skydoc/fakebuildapi/FakeSkylarkNativeModuleApi.java
@@ -20,6 +20,7 @@
 import com.google.devtools.build.lib.skylarkbuildapi.SkylarkNativeModuleApi;
 import com.google.devtools.build.lib.syntax.ClassObject;
 import com.google.devtools.build.lib.syntax.EvalException;
+import com.google.devtools.build.lib.syntax.FuncallExpression;
 import com.google.devtools.build.lib.syntax.Runtime.NoneType;
 import com.google.devtools.build.lib.syntax.SkylarkDict;
 import com.google.devtools.build.lib.syntax.SkylarkList;
@@ -36,21 +37,21 @@
       SkylarkList<?> exclude,
       Integer excludeDirectories,
       Object allowEmpty,
-      Location loc,
+      FuncallExpression ast,
       StarlarkThread thread)
       throws EvalException, InterruptedException {
     return MutableList.of(thread);
   }
 
   @Override
-  public Object existingRule(String name, Location loc, StarlarkThread thread)
+  public Object existingRule(String name, FuncallExpression ast, StarlarkThread thread)
       throws EvalException, InterruptedException {
     return null;
   }
 
   @Override
   public SkylarkDict<String, SkylarkDict<String, Object>> existingRules(
-      Location loc, StarlarkThread thread) throws EvalException, InterruptedException {
+      FuncallExpression ast, StarlarkThread thread) throws EvalException, InterruptedException {
     return SkylarkDict.of(thread);
   }
 
@@ -59,7 +60,7 @@
       String name,
       SkylarkList<?> packages,
       SkylarkList<?> includes,
-      Location loc,
+      FuncallExpression ast,
       StarlarkThread thread)
       throws EvalException {
     return null;
@@ -67,13 +68,17 @@
 
   @Override
   public NoneType exportsFiles(
-      SkylarkList<?> srcs, Object visibility, Object licenses, Location loc, StarlarkThread thread)
+      SkylarkList<?> srcs,
+      Object visibility,
+      Object licenses,
+      FuncallExpression ast,
+      StarlarkThread thread)
       throws EvalException {
     return null;
   }
 
   @Override
-  public String packageName(Location loc, StarlarkThread thread) throws EvalException {
+  public String packageName(FuncallExpression ast, StarlarkThread thread) throws EvalException {
     return "";
   }
 
diff --git a/src/test/java/com/google/devtools/build/lib/packages/PackageFactoryTest.java b/src/test/java/com/google/devtools/build/lib/packages/PackageFactoryTest.java
index 821ad38..dc91321 100644
--- a/src/test/java/com/google/devtools/build/lib/packages/PackageFactoryTest.java
+++ b/src/test/java/com/google/devtools/build/lib/packages/PackageFactoryTest.java
@@ -661,7 +661,9 @@
     events.setFailFast(false);
     assertGlobFails(
         "glob(1, exclude=2)",
-        "expected sequence of strings for 'include' while calling glob but got int instead: 1");
+        "argument 'include' has type 'int', but should be 'sequence'\n"
+            + "in call to builtin function glob(include, *, exclude, exclude_directories, "
+            + "allow_empty)");
   }
 
   @Test