Create an experimental analysis_test parameter in rule

This will control whether a given rule should be treated as an "analysis test" rule. The parameter and its functionality will only be available via --experimental_analysis_testing_improvements until it is complete.

In this change, analysis_test = True enforces the restriction that the rule implementation function for that rule may not register actions.

See https://docs.google.com/document/d/17P2sgC6VPmcA7CcqC2p4cfU2Kaj6dwKiQfIXhtXTzl4/ for
details.

Progress toward #6237

RELNOTES: None.
PiperOrigin-RevId: 217562194
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/RuleConfiguredTargetBuilder.java b/src/main/java/com/google/devtools/build/lib/analysis/RuleConfiguredTargetBuilder.java
index 15ad36a..1679f60 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/RuleConfiguredTargetBuilder.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/RuleConfiguredTargetBuilder.java
@@ -151,6 +151,9 @@
     GeneratingActions generatingActions =
         Actions.filterSharedActionsAndThrowActionConflict(
             analysisEnvironment.getActionKeyContext(), analysisEnvironment.getRegisteredActions());
+    if (ruleContext.getRule().isAnalysisTest()) {
+      Preconditions.checkState(generatingActions.getActions().isEmpty());
+    }
     return new RuleConfiguredTarget(
         ruleContext,
         providers,
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/skylark/SkylarkActionFactory.java b/src/main/java/com/google/devtools/build/lib/analysis/skylark/SkylarkActionFactory.java
index b4b0eda..c3562c2 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/skylark/SkylarkActionFactory.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/skylark/SkylarkActionFactory.java
@@ -22,7 +22,9 @@
 import com.google.common.collect.ImmutableSet;
 import com.google.devtools.build.lib.actions.Action;
 import com.google.devtools.build.lib.actions.ActionAnalysisMetadata;
+import com.google.devtools.build.lib.actions.ActionRegistry;
 import com.google.devtools.build.lib.actions.Artifact;
+import com.google.devtools.build.lib.actions.ArtifactOwner;
 import com.google.devtools.build.lib.actions.ArtifactRoot;
 import com.google.devtools.build.lib.actions.CommandLine;
 import com.google.devtools.build.lib.actions.CommandLineExpansionException;
@@ -100,6 +102,31 @@
         : ruleContext.getBinOrGenfilesDirectory();
   }
 
+  /**
+   * Returns a {@link ActionRegistry} object to register actions using this action factory.
+   *
+   * @throws EvalException if actions cannot be registered with this object
+   */
+  public ActionRegistry asActionRegistry(
+      final Location location, SkylarkActionFactory skylarkActionFactory) throws EvalException {
+    validateActionCreation(location);
+    return new ActionRegistry() {
+
+      @Override
+      public void registerAction(ActionAnalysisMetadata... actions) {
+        ruleContext.registerAction(actions);
+      }
+
+      @Override
+      public ArtifactOwner getOwner() {
+        return skylarkActionFactory
+            .getActionConstructionContext()
+            .getAnalysisEnvironment()
+            .getOwner();
+      }
+    };
+  }
+
   @Override
   public Artifact declareFile(String filename, Object sibling) throws EvalException {
     context.checkMutable("actions.declare_file");
@@ -134,7 +161,7 @@
   }
 
   @Override
-  public void doNothing(String mnemonic, Object inputs) throws EvalException {
+  public void doNothing(String mnemonic, Object inputs, Location location) throws EvalException {
     context.checkMutable("actions.do_nothing");
     NestedSet<Artifact> inputSet = inputs instanceof SkylarkNestedSet
         ? ((SkylarkNestedSet) inputs).getSet(Artifact.class)
@@ -152,7 +179,7 @@
             mnemonic,
             SPAWN_INFO,
             SpawnInfo.newBuilder().build());
-    ruleContext.registerAction(action);
+    registerAction(location, action);
   }
 
   @AutoCodec @AutoCodec.VisibleForSerialization
@@ -160,7 +187,8 @@
       SpawnInfo.spawnInfo;
 
   @Override
-  public void write(FileApi output, Object content, Boolean isExecutable) throws EvalException {
+  public void write(FileApi output, Object content, Boolean isExecutable, Location location)
+      throws EvalException {
     context.checkMutable("actions.write");
     final Action action;
     if (content instanceof String) {
@@ -181,7 +209,7 @@
     } else {
       throw new AssertionError("Unexpected type: " + content.getClass().getSimpleName());
     }
-    ruleContext.registerAction(action);
+    registerAction(location, action);
   }
 
   @Override
@@ -236,19 +264,31 @@
         builder);
   }
 
+  private void validateActionCreation(Location location) throws EvalException {
+    if (ruleContext.getRule().isAnalysisTest()) {
+      throw new EvalException(
+          location,
+          "implementation function of a rule with "
+              + "analysis_test=true may not register actions. Analysis test rules may only return "
+              + "success/failure information via AnalysisTestResultInfo.");
+    }
+  }
+
   /**
    * Registers actions in the context of this {@link SkylarkActionFactory}.
    *
-   * Use {@link #getActionConstructionContext()} to obtain the context required to
-   * create those actions.
+   * <p>Use {@link #getActionConstructionContext()} to obtain the context required to create those
+   * actions.
    */
-  public void registerAction(ActionAnalysisMetadata... actions) {
+  public void registerAction(Location location, ActionAnalysisMetadata... actions)
+      throws EvalException {
+    validateActionCreation(location);
     ruleContext.registerAction(actions);
   }
 
   /**
-   * Returns information needed to construct actions that can be
-   * registered with {@link #registerAction(ActionAnalysisMetadata...)}.
+   * Returns information needed to construct actions that can be registered with {@link
+   * #registerAction(Location, ActionAnalysisMetadata...)}.
    */
   public ActionConstructionContext getActionConstructionContext() {
     return ruleContext;
@@ -487,7 +527,7 @@
       }
     }
     // Always register the action
-    ruleContext.registerAction(builder.build(ruleContext));
+    registerAction(location, builder.build(ruleContext));
   }
 
   private String getMnemonic(Object mnemonicUnchecked) {
@@ -508,7 +548,8 @@
       FileApi template,
       FileApi output,
       SkylarkDict<?, ?> substitutionsUnchecked,
-      Boolean executable)
+      Boolean executable,
+      Location location)
       throws EvalException {
     context.checkMutable("actions.expand_template");
     ImmutableList.Builder<Substitution> substitutionsBuilder = ImmutableList.builder();
@@ -531,7 +572,7 @@
             (Artifact) output,
             substitutionsBuilder.build(),
             executable);
-    ruleContext.registerAction(action);
+    registerAction(location, action);
   }
 
   /**
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/skylark/SkylarkRuleClassFunctions.java b/src/main/java/com/google/devtools/build/lib/analysis/skylark/SkylarkRuleClassFunctions.java
index eb1fe59..fa17854 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/skylark/SkylarkRuleClassFunctions.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/skylark/SkylarkRuleClassFunctions.java
@@ -271,10 +271,21 @@
       SkylarkList<?> providesArg,
       Boolean executionPlatformConstraintsAllowed,
       SkylarkList<?> execCompatibleWith,
+      Object analysisTest,
       FuncallExpression ast,
       Environment funcallEnv)
       throws EvalException, ConversionException {
     SkylarkUtils.checkLoadingOrWorkspacePhase(funcallEnv, "rule", ast.getLocation());
+
+    if (analysisTest != Runtime.UNBOUND
+        && !funcallEnv.getSemantics().experimentalAnalysisTestingImprovements()) {
+      throw new EvalException(
+          ast.getLocation(),
+          "analysis_test parameter is experimental and not available for "
+              + "general use. It is subject to change at any time. It may be enabled by specifying "
+              + "--experimental_analysis_testing_improvements");
+    }
+
     RuleClassType type = test ? RuleClassType.TEST : RuleClassType.NORMAL;
     RuleClass parent =
         test
@@ -289,6 +300,9 @@
     if (skylarkTestable) {
       builder.setSkylarkTestable();
     }
+    if (Boolean.TRUE.equals(analysisTest)) {
+      builder.setIsAnalysisTest();
+    }
 
     if (executable || test) {
       addAttribute(
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/skylark/SkylarkRuleContext.java b/src/main/java/com/google/devtools/build/lib/analysis/skylark/SkylarkRuleContext.java
index 2e24c3c..c647f87 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/skylark/SkylarkRuleContext.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/skylark/SkylarkRuleContext.java
@@ -884,7 +884,7 @@
       throws EvalException {
     checkDeprecated("ctx.actions.write", "ctx.file_action", loc, env.getSemantics());
     checkMutable("file_action");
-    actions().write(output, content, executable);
+    actions().write(output, content, executable, loc);
     return Runtime.NONE;
   }
 
@@ -893,7 +893,7 @@
       throws EvalException {
     checkDeprecated("ctx.actions.do_nothing", "ctx.empty_action", loc, env.getSemantics());
     checkMutable("empty_action");
-    actions().doNothing(mnemonic, inputs);
+    actions().doNothing(mnemonic, inputs, loc);
     return Runtime.NONE;
   }
 
@@ -908,7 +908,7 @@
       throws EvalException {
     checkDeprecated("ctx.actions.expand_template", "ctx.template_action", loc, env.getSemantics());
     checkMutable("template_action");
-    actions().expandTemplate(template, output, substitutionsUnchecked, executable);
+    actions().expandTemplate(template, output, substitutionsUnchecked, executable, loc);
     return Runtime.NONE;
   }
 
diff --git a/src/main/java/com/google/devtools/build/lib/packages/Rule.java b/src/main/java/com/google/devtools/build/lib/packages/Rule.java
index 63eca18..00ee63e 100644
--- a/src/main/java/com/google/devtools/build/lib/packages/Rule.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/Rule.java
@@ -176,6 +176,11 @@
         : ruleClass.hasBinaryOutput();
   }
 
+  /** Returns true if this rule is an analysis test (set by analysis_test = true). */
+  public boolean isAnalysisTest() {
+    return ruleClass.isAnalysisTest();
+  }
+
   /**
    * Returns true iff there were errors while constructing this rule, such as
    * attributes with missing values or values of the wrong type.
diff --git a/src/main/java/com/google/devtools/build/lib/packages/RuleClass.java b/src/main/java/com/google/devtools/build/lib/packages/RuleClass.java
index 72d5401..753b73e 100644
--- a/src/main/java/com/google/devtools/build/lib/packages/RuleClass.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/RuleClass.java
@@ -630,6 +630,7 @@
     private boolean binaryOutput = true;
     private boolean workspaceOnly = false;
     private boolean isExecutableSkylark = false;
+    private boolean isAnalysisTest = false;
     private boolean isConfigMatcher = false;
     private boolean hasFunctionTransitionWhitelist = false;
     private ImplicitOutputsFunction implicitOutputsFunction = ImplicitOutputsFunction.NONE;
@@ -781,6 +782,7 @@
           binaryOutput,
           workspaceOnly,
           isExecutableSkylark,
+          isAnalysisTest,
           hasFunctionTransitionWhitelist,
           implicitOutputsFunction,
           isConfigMatcher,
@@ -1168,6 +1170,12 @@
       return this;
     }
 
+    /** This rule class is marked as an analysis test. */
+    public Builder setIsAnalysisTest() {
+      this.isAnalysisTest = true;
+      return this;
+    }
+
     /**
      * This rule class has the _whitelist_function_transition attribute.  Intended only for Skylark
      * rules.
@@ -1349,6 +1357,7 @@
   private final boolean binaryOutput;
   private final boolean workspaceOnly;
   private final boolean isExecutableSkylark;
+  private final boolean isAnalysisTest;
   private final boolean isConfigMatcher;
   private final boolean hasFunctionTransitionWhitelist;
 
@@ -1470,6 +1479,7 @@
       boolean binaryOutput,
       boolean workspaceOnly,
       boolean isExecutableSkylark,
+      boolean isAnalysisTest,
       boolean hasFunctionTransitionWhitelist,
       ImplicitOutputsFunction implicitOutputsFunction,
       boolean isConfigMatcher,
@@ -1489,7 +1499,7 @@
       boolean supportsPlatforms,
       ExecutionPlatformConstraintsAllowed executionPlatformConstraintsAllowed,
       Set<Label> executionPlatformConstraints,
-      OutputFile.Kind  outputFileKind,
+      OutputFile.Kind outputFileKind,
       Collection<Attribute> attributes) {
     this.name = name;
     this.key = key;
@@ -1517,6 +1527,7 @@
     this.attributes = ImmutableList.copyOf(attributes);
     this.workspaceOnly = workspaceOnly;
     this.isExecutableSkylark = isExecutableSkylark;
+    this.isAnalysisTest = isAnalysisTest;
     this.hasFunctionTransitionWhitelist = hasFunctionTransitionWhitelist;
     this.configurationFragmentPolicy = configurationFragmentPolicy;
     this.supportsConstraintChecking = supportsConstraintChecking;
@@ -2343,6 +2354,11 @@
     return isExecutableSkylark;
   }
 
+  /** Returns true if this rule class is an analysis test (set by analysis_test = true). */
+  public boolean isAnalysisTest() {
+    return isAnalysisTest;
+  }
+
   /**
    * Returns true if this rule class has the _whitelist_function_transition attribute.
    */
diff --git a/src/main/java/com/google/devtools/build/lib/rules/java/JavaInfoBuildHelper.java b/src/main/java/com/google/devtools/build/lib/rules/java/JavaInfoBuildHelper.java
index 42b773d..a73db3f 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/java/JavaInfoBuildHelper.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/java/JavaInfoBuildHelper.java
@@ -22,10 +22,8 @@
 import com.google.common.base.Ascii;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Iterables;
-import com.google.devtools.build.lib.actions.ActionAnalysisMetadata;
 import com.google.devtools.build.lib.actions.ActionRegistry;
 import com.google.devtools.build.lib.actions.Artifact;
-import com.google.devtools.build.lib.actions.ArtifactOwner;
 import com.google.devtools.build.lib.analysis.ConfiguredTarget;
 import com.google.devtools.build.lib.analysis.FilesToRunProvider;
 import com.google.devtools.build.lib.analysis.Runfiles;
@@ -120,7 +118,8 @@
               sourceFiles,
               sourceJars,
               (ConfiguredTarget) javaToolchain,
-              (ConfiguredTarget) hostJavabase);
+              (ConfiguredTarget) hostJavabase,
+              location);
     }
     final Artifact iJar;
     if (useIjar) {
@@ -136,7 +135,11 @@
       }
       iJar =
           buildIjar(
-              (SkylarkActionFactory) actions, outputJar, null, (ConfiguredTarget) javaToolchain);
+              (SkylarkActionFactory) actions,
+              outputJar,
+              null,
+              (ConfiguredTarget) javaToolchain,
+              location);
     } else {
       iJar = outputJar;
     }
@@ -241,7 +244,8 @@
       SkylarkList<Artifact> sourceFiles,
       SkylarkList<Artifact> sourceJars,
       ConfiguredTarget javaToolchain,
-      ConfiguredTarget hostJavabase)
+      ConfiguredTarget hostJavabase,
+      Location location)
       throws EvalException {
     // No sources to pack, return None
     if (sourceFiles.isEmpty() && sourceJars.isEmpty()) {
@@ -251,7 +255,7 @@
     if (sourceFiles.isEmpty() && sourceJars.size() == 1) {
       return sourceJars.get(0);
     }
-    ActionRegistry actionRegistry = createActionRegistry(actions);
+    ActionRegistry actionRegistry = actions.asActionRegistry(location, actions);
     Artifact outputSrcJar = getSourceJar(actions.getActionConstructionContext(), outputJar);
     JavaRuntimeInfo javaRuntimeInfo = JavaRuntimeInfo.from(hostJavabase, null);
     JavaToolchainProvider javaToolchainProvider = getJavaToolchainProvider(javaToolchain);
@@ -268,24 +272,6 @@
     return outputSrcJar;
   }
 
-  private ActionRegistry createActionRegistry(SkylarkActionFactory skylarkActionFactory) {
-    return new ActionRegistry() {
-
-      @Override
-      public void registerAction(ActionAnalysisMetadata... actions) {
-        skylarkActionFactory.registerAction(actions);
-      }
-
-      @Override
-      public ArtifactOwner getOwner() {
-        return skylarkActionFactory
-            .getActionConstructionContext()
-            .getAnalysisEnvironment()
-            .getOwner();
-      }
-    };
-  }
-
   /** Creates a {@link JavaSourceJarsProvider} from the given lists of source jars. */
   private static JavaSourceJarsProvider createJavaSourceJarsProvider(
       List<Artifact> sourceJars, NestedSet<Artifact> transitiveSourceJars) {
@@ -384,7 +370,8 @@
                 (SkylarkActionFactory) actions,
                 compileJar,
                 null,
-                (ConfiguredTarget) javaToolchain));
+                (ConfiguredTarget) javaToolchain,
+                location));
       }
       javaCompilationArgsBuilder.addDirectCompileTimeJars(
           /* interfaceJars = */ builder.build(), /* fullJars= */ compileTimeJars);
@@ -558,7 +545,8 @@
       SkylarkActionFactory actions,
       Artifact inputJar,
       @Nullable Label targetLabel,
-      ConfiguredTarget javaToolchainConfiguredTarget)
+      ConfiguredTarget javaToolchainConfiguredTarget,
+      Location location)
       throws EvalException {
     String ijarBasename = FileSystemUtils.removeExtension(inputJar.getFilename()) + "-ijar.jar";
     Artifact interfaceJar = actions.declareFile(ijarBasename, inputJar);
@@ -578,7 +566,7 @@
             .addCommandLine(commandLine.build())
             .useDefaultShellEnvironment()
             .setMnemonic("JavaIjar");
-    actions.registerAction(actionBuilder.build(actions.getActionConstructionContext()));
+    actions.registerAction(location, actionBuilder.build(actions.getActionConstructionContext()));
     return interfaceJar;
   }
 
@@ -586,7 +574,8 @@
       SkylarkActionFactory actions,
       Artifact inputJar,
       Label targetLabel,
-      ConfiguredTarget javaToolchainConfiguredTarget)
+      ConfiguredTarget javaToolchainConfiguredTarget,
+      Location location)
       throws EvalException {
     String basename = FileSystemUtils.removeExtension(inputJar.getFilename()) + "-stamped.jar";
     Artifact outputJar = actions.declareFile(basename, inputJar);
@@ -608,7 +597,7 @@
             .addCommandLine(commandLine.build())
             .useDefaultShellEnvironment()
             .setMnemonic("JavaIjar");
-    actions.registerAction(actionBuilder.build(actions.getActionConstructionContext()));
+    actions.registerAction(location, actionBuilder.build(actions.getActionConstructionContext()));
     return outputJar;
   }
 
diff --git a/src/main/java/com/google/devtools/build/lib/rules/java/JavaSkylarkCommon.java b/src/main/java/com/google/devtools/build/lib/rules/java/JavaSkylarkCommon.java
index 1a7d590..29a389e 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/java/JavaSkylarkCommon.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/java/JavaSkylarkCommon.java
@@ -126,18 +126,28 @@
       SkylarkActionFactory actions,
       Artifact jar,
       Object targetLabel,
-      ConfiguredTarget javaToolchain)
+      ConfiguredTarget javaToolchain,
+      Location location)
       throws EvalException {
     return JavaInfoBuildHelper.getInstance()
         .buildIjar(
-            actions, jar, targetLabel != Runtime.NONE ? (Label) targetLabel : null, javaToolchain);
+            actions,
+            jar,
+            targetLabel != Runtime.NONE ? (Label) targetLabel : null,
+            javaToolchain,
+            location);
   }
 
   @Override
   public Artifact stampJar(
-      SkylarkActionFactory actions, Artifact jar, Label targetLabel, ConfiguredTarget javaToolchain)
+      SkylarkActionFactory actions,
+      Artifact jar,
+      Label targetLabel,
+      ConfiguredTarget javaToolchain,
+      Location location)
       throws EvalException {
-    return JavaInfoBuildHelper.getInstance().stampJar(actions, jar, targetLabel, javaToolchain);
+    return JavaInfoBuildHelper.getInstance()
+        .stampJar(actions, jar, targetLabel, javaToolchain, location);
   }
 
   @Override
@@ -147,10 +157,12 @@
       SkylarkList<Artifact> sourceFiles,
       SkylarkList<Artifact> sourceJars,
       ConfiguredTarget javaToolchain,
-      ConfiguredTarget hostJavabase)
+      ConfiguredTarget hostJavabase,
+      Location location)
       throws EvalException {
     return JavaInfoBuildHelper.getInstance()
-        .packSourceFiles(actions, outputJar, sourceFiles, sourceJars, javaToolchain, hostJavabase);
+        .packSourceFiles(
+            actions, outputJar, sourceFiles, sourceJars, javaToolchain, hostJavabase, location);
   }
 
   @Override
diff --git a/src/main/java/com/google/devtools/build/lib/skylarkbuildapi/SkylarkActionFactoryApi.java b/src/main/java/com/google/devtools/build/lib/skylarkbuildapi/SkylarkActionFactoryApi.java
index a48e067..aaef112 100644
--- a/src/main/java/com/google/devtools/build/lib/skylarkbuildapi/SkylarkActionFactoryApi.java
+++ b/src/main/java/com/google/devtools/build/lib/skylarkbuildapi/SkylarkActionFactoryApi.java
@@ -122,41 +122,41 @@
             positional = false,
             defaultValue = "[]",
             doc = "List of the input files of the action."),
-      })
-  public void doNothing(String mnemonic, Object inputs) throws EvalException;
+      },
+      useLocation = true)
+  public void doNothing(String mnemonic, Object inputs, Location location) throws EvalException;
 
   @SkylarkCallable(
-    name = "write",
-    doc =
-        "Creates a file write action. When the action is executed, it will write the given content "
-            + "to a file. This is used to generate files using information available in the "
-            + "analysis phase. If the file is large and with a lot of static content, consider "
-            + "using <a href=\"#expand_template\"><code>expand_template</code></a>.",
-    parameters = {
-      @Param(name = "output", type = FileApi.class, doc = "The output file.", named = true),
-      @Param(
-        name = "content",
-        type = Object.class,
-        allowedTypes = {
-          @ParamType(type = String.class),
-          @ParamType(type = CommandLineArgsApi.class)
-        },
-        doc =
-            "the contents of the file. "
-                + "May be a either a string or an "
-                + "<a href=\"actions.html#args\"><code>actions.args()</code></a> object.",
-        named = true
-      ),
-      @Param(
-        name = "is_executable",
-        type = Boolean.class,
-        defaultValue = "False",
-        doc = "Whether the output file should be executable.",
-        named = true
-      )
-    }
-  )
-  public void write(FileApi output, Object content, Boolean isExecutable) throws EvalException;
+      name = "write",
+      doc =
+          "Creates a file write action. When the action is executed, it will write the given "
+              + "content to a file. This is used to generate files using information available in "
+              + "the analysis phase. If the file is large and with a lot of static content, "
+              + "consider using <a href=\"#expand_template\"><code>expand_template</code></a>.",
+      parameters = {
+        @Param(name = "output", type = FileApi.class, doc = "The output file.", named = true),
+        @Param(
+            name = "content",
+            type = Object.class,
+            allowedTypes = {
+              @ParamType(type = String.class),
+              @ParamType(type = CommandLineArgsApi.class)
+            },
+            doc =
+                "the contents of the file. "
+                    + "May be a either a string or an "
+                    + "<a href=\"actions.html#args\"><code>actions.args()</code></a> object.",
+            named = true),
+        @Param(
+            name = "is_executable",
+            type = Boolean.class,
+            defaultValue = "False",
+            doc = "Whether the output file should be executable.",
+            named = true)
+      },
+      useLocation = true)
+  public void write(FileApi output, Object content, Boolean isExecutable, Location location)
+      throws EvalException;
 
   @SkylarkCallable(
       name = "run",
@@ -500,12 +500,14 @@
             named = true,
             positional = false,
             doc = "Whether the output file should be executable.")
-      })
+      },
+      useLocation = true)
   public void expandTemplate(
       FileApi template,
       FileApi output,
       SkylarkDict<?, ?> substitutionsUnchecked,
-      Boolean executable)
+      Boolean executable,
+      Location location)
       throws EvalException;
 
   @SkylarkCallable(
diff --git a/src/main/java/com/google/devtools/build/lib/skylarkbuildapi/SkylarkRuleFunctionsApi.java b/src/main/java/com/google/devtools/build/lib/skylarkbuildapi/SkylarkRuleFunctionsApi.java
index 6454900..2f2c008 100644
--- a/src/main/java/com/google/devtools/build/lib/skylarkbuildapi/SkylarkRuleFunctionsApi.java
+++ b/src/main/java/com/google/devtools/build/lib/skylarkbuildapi/SkylarkRuleFunctionsApi.java
@@ -25,6 +25,7 @@
 import com.google.devtools.build.lib.syntax.Environment;
 import com.google.devtools.build.lib.syntax.EvalException;
 import com.google.devtools.build.lib.syntax.FuncallExpression;
+import com.google.devtools.build.lib.syntax.Runtime.UnboundMarker;
 import com.google.devtools.build.lib.syntax.SkylarkDict;
 import com.google.devtools.build.lib.syntax.SkylarkList;
 
@@ -89,219 +90,226 @@
   public ProviderApi provider(String doc, Object fields, Location location) throws EvalException;
 
   @SkylarkCallable(
-    name = "rule",
-    doc =
-        "Creates a new rule, which can be called from a BUILD file or a macro to create targets."
-            + "<p>Rules must be assigned to global variables in a .bzl file; the name of the "
-            + "global variable is the rule's name."
-            + "<p>Test rules are required to have a name ending in <code>_test</code>, while all "
-            + "other rules must not have this suffix. (This restriction applies only to rules, not "
-            + "to their targets.)",
-    parameters = {
-      @Param(
-        name = "implementation",
-        type = BaseFunction.class,
-        legacyNamed = true,
-        doc =
-            "the function implementing this rule, must have exactly one parameter: "
-                + "<a href=\"ctx.html\">ctx</a>. The function is called during the analysis "
-                + "phase for each instance of the rule. It can access the attributes "
-                + "provided by the user. It must create actions to generate all the declared "
-                + "outputs."
-      ),
-      @Param(
-        name = "test",
-        type = Boolean.class,
-        legacyNamed = true,
-        defaultValue = "False",
-        doc =
-            "Whether this rule is a test rule, that is, whether it may be the subject of a "
-                + "<code>blaze test</code> command. All test rules are automatically considered "
-                + "<a href='#rule.executable'>executable</a>; it is unnecessary (and discouraged) "
-                + "to explicitly set <code>executable = True</code> for a test rule. See the "
-                + "<a href='../rules.$DOC_EXT#executable-rules-and-test-rules'>Rules page</a> for "
-                + "more information."
-      ),
-      @Param(
-        name = "attrs",
-        type = SkylarkDict.class,
-        legacyNamed = true,
-        noneable = true,
-        defaultValue = "None",
-        doc =
-            "dictionary to declare all the attributes of the rule. It maps from an attribute "
-                + "name to an attribute object (see <a href=\"attr.html\">attr</a> module). "
-                + "Attributes starting with <code>_</code> are private, and can be used to "
-                + "add an implicit dependency on a label. The attribute <code>name</code> is "
-                + "implicitly added and must not be specified. Attributes "
-                + "<code>visibility</code>, <code>deprecation</code>, <code>tags</code>, "
-                + "<code>testonly</code>, and <code>features</code> are implicitly added and "
-                + "cannot be overridden."
-      ),
-      // TODO(bazel-team): need to give the types of these builtin attributes
-      @Param(
-        name = "outputs",
-        type = SkylarkDict.class,
-        legacyNamed = true,
-        callbackEnabled = true,
-        noneable = true,
-        defaultValue = "None",
-        doc =
-            "<b>Experimental:</b> This API is in the process of being redesigned."
-                + "<p>A schema for defining predeclared outputs. Unlike <a href='attr.html#output'>"
-                + "<code>output</code></a> and <a href='attr.html#output_list'><code>output_list"
-                + "</code></a> attributes, the user does not specify the labels for these files. "
-                + "See the <a href='../rules.$DOC_EXT#files'>Rules page</a> for more on "
-                + "predeclared outputs."
-                + "<p>The value of this argument is either a dictionary or a callback function "
-                + "that produces a dictionary. The callback works similar to computed dependency "
-                + "attributes: The function's parameter names are matched against the rule's "
-                + "attributes, so for example if you pass <code>outputs = _my_func</code> with the "
-                + "definition <code>def _my_func(srcs, deps): ...</code>, the function has access "
-                + "to the attributes <code>srcs</code> and <code>deps</code>. Whether the "
-                + "dictionary is specified directly or via a function, it is interpreted as "
-                + "follows."
-                + "<p>Each entry in the dictionary creates a predeclared output where the key is "
-                + "an identifier and the value is a string template that determines the output's "
-                + "label. In the rule's implementation function, the identifier becomes the field "
-                + "name used to access the output's <a href='File.html'><code>File</code></a> in "
-                + "<a href='ctx.html#outputs'><code>ctx.outputs</code></a>. The output's label has "
-                + "the same package as the rule, and the part after the package is produced by "
-                + "substituting each placeholder of the form <code>\"%{ATTR}\"</code> with a "
-                + "string formed from the value of the attribute <code>ATTR</code>:"
-                + "<ul>"
-                + "<li>String-typed attributes are substituted verbatim."
-                + "<li>Label-typed attributes become the part of the label after the package, "
-                + "minus the file extension. For example, the label <code>\"//pkg:a/b.c\"</code> "
-                + "becomes <code>\"a/b\"</code>."
-                + "<li>Output-typed attributes become the part of the label after the package, "
-                + "including the file extension (for the above example, <code>\"a/b.c\"</code>)."
-                + "<li>All list-typed attributes (for example, <code>attr.label_list</code>) used "
-                + "in placeholders are required to have <i>exactly one element</i>. Their "
-                + "conversion is the same as their non-list version (<code>attr.label</code>)."
-                + "<li>Other attribute types may not appear in placeholders."
-                + "<li>The special non-attribute placeholders <code>%{dirname}</code> and <code>"
-                + "%{basename}</code> expand to those parts of the rule's label, excluding its "
-                + "package. For example, in <code>\"//pkg:a/b.c\"</code>, the dirname is <code>"
-                + "a</code> and the basename is <code>b.c</code>."
-                + "</ul>"
-                + "<p>In practice, the most common substitution placeholder is "
-                + "<code>\"%{name}\"</code>. For example, for a target named \"foo\", the outputs "
-                + "dict <code>{\"bin\": \"%{name}.exe\"}</code> predeclares an output named "
-                + "<code>foo.exe</code> that is accessible in the implementation function as "
-                + "<code>ctx.outputs.bin</code>."
-      ),
-      @Param(
-        name = "executable",
-        type = Boolean.class,
-        legacyNamed = true,
-        defaultValue = "False",
-        doc =
-            "Whether this rule is considered executable, that is, whether it may be the subject of "
-                + "a <code>blaze run</code> command. See the "
-                + "<a href='../rules.$DOC_EXT#executable-rules-and-test-rules'>Rules page</a> for "
-                + "more information."
-      ),
-      @Param(
-        name = "output_to_genfiles",
-        type = Boolean.class,
-        legacyNamed = true,
-        defaultValue = "False",
-        doc =
-            "If true, the files will be generated in the genfiles directory instead of the "
-                + "bin directory. Unless you need it for compatibility with existing rules "
-                + "(e.g. when generating header files for C++), do not set this flag."
-      ),
-      @Param(
-        name = "fragments",
-        type = SkylarkList.class,
-        legacyNamed = true,
-        generic1 = String.class,
-        defaultValue = "[]",
-        doc =
-            "List of names of configuration fragments that the rule requires "
-                + "in target configuration."
-      ),
-      @Param(
-        name = "host_fragments",
-        type = SkylarkList.class,
-        legacyNamed = true,
-        generic1 = String.class,
-        defaultValue = "[]",
-        doc =
-            "List of names of configuration fragments that the rule requires "
-                + "in host configuration."
-      ),
-      @Param(
-        name = "_skylark_testable",
-        type = Boolean.class,
-        legacyNamed = true,
-        defaultValue = "False",
-        doc =
-            "<i>(Experimental)</i><br/><br/>"
-                + "If true, this rule will expose its actions for inspection by rules that "
-                + "depend on it via an <a href=\"globals.html#Actions\">Actions</a> "
-                + "provider. The provider is also available to the rule itself by calling "
-                + "<a href=\"ctx.html#created_actions\">ctx.created_actions()</a>."
-                + "<br/><br/>"
-                + "This should only be used for testing the analysis-time behavior of "
-                + "Skylark rules. This flag may be removed in the future."
-      ),
-      @Param(
-        name = "toolchains",
-        type = SkylarkList.class,
-        legacyNamed = true,
-        generic1 = String.class,
-        defaultValue = "[]",
-        doc =
-            "<i>(Experimental)</i><br/><br/>"
-                + "If set, the set of toolchains this rule requires. Toolchains will be "
-                + "found by checking the current platform, and provided to the rule "
-                + "implementation via <code>ctx.toolchain</code>."
-      ),
-      @Param(
-        name = "doc",
-        type = String.class,
-        legacyNamed = true,
-        defaultValue = "''",
-        doc = "A description of the rule that can be extracted by documentation generating tools."
-      ),
-      @Param(
-        name = "provides",
-        type = SkylarkList.class,
-        named = true,
-        positional = false,
-        defaultValue = "[]",
-        doc = PROVIDES_DOC
-      ),
-      @Param(
-        name = "execution_platform_constraints_allowed",
-        type = Boolean.class,
-        named = true,
-        positional = false,
-        defaultValue = "False",
-        doc =
-            "If true, a special attribute named <code>exec_compatible_with</code> of "
-                + "label-list type is added, which must not already exist in "
-                + "<code>attrs</code>. Targets may use this attribute to specify additional "
-                + "constraints on the execution platform beyond those given in the "
-                + "<code>exec_compatible_with</code> argument to <code>rule()</code>."
-      ),
-      @Param(
-        name = "exec_compatible_with",
-        type = SkylarkList.class,
-        generic1 = String.class,
-        named = true,
-        positional = false,
-        defaultValue = "[]",
-        doc =
-            "A list of constraints on the execution platform that apply to all targets of "
-                + "this rule type."
-      )
-    },
-    useAst = true,
-    useEnvironment = true
-  )
+      name = "rule",
+      doc =
+          "Creates a new rule, which can be called from a BUILD file or a macro to create targets."
+              + "<p>Rules must be assigned to global variables in a .bzl file; the name of the "
+              + "global variable is the rule's name."
+              + "<p>Test rules are required to have a name ending in <code>_test</code>, while all "
+              + "other rules must not have this suffix. (This restriction applies only to rules, "
+              + "not to their targets.)",
+      parameters = {
+        @Param(
+            name = "implementation",
+            type = BaseFunction.class,
+            legacyNamed = true,
+            doc =
+                "the function implementing this rule, must have exactly one parameter: "
+                    + "<a href=\"ctx.html\">ctx</a>. The function is called during the analysis "
+                    + "phase for each instance of the rule. It can access the attributes "
+                    + "provided by the user. It must create actions to generate all the declared "
+                    + "outputs."),
+        @Param(
+            name = "test",
+            type = Boolean.class,
+            legacyNamed = true,
+            defaultValue = "False",
+            doc =
+                "Whether this rule is a test rule, that is, whether it may be the subject of a "
+                    + "<code>blaze test</code> command. All test rules are automatically "
+                    + "considered <a href='#rule.executable'>executable</a>; it is unnecessary "
+                    + "(and discouraged) to explicitly set <code>executable = True</code> for a "
+                    + "test rule. See the "
+                    + "<a href='../rules.$DOC_EXT#executable-rules-and-test-rules'>Rules page</a> "
+                    + "for more information."),
+        @Param(
+            name = "attrs",
+            type = SkylarkDict.class,
+            legacyNamed = true,
+            noneable = true,
+            defaultValue = "None",
+            doc =
+                "dictionary to declare all the attributes of the rule. It maps from an attribute "
+                    + "name to an attribute object (see <a href=\"attr.html\">attr</a> module). "
+                    + "Attributes starting with <code>_</code> are private, and can be used to "
+                    + "add an implicit dependency on a label. The attribute <code>name</code> is "
+                    + "implicitly added and must not be specified. Attributes "
+                    + "<code>visibility</code>, <code>deprecation</code>, <code>tags</code>, "
+                    + "<code>testonly</code>, and <code>features</code> are implicitly added and "
+                    + "cannot be overridden."),
+        // TODO(bazel-team): need to give the types of these builtin attributes
+        @Param(
+            name = "outputs",
+            type = SkylarkDict.class,
+            legacyNamed = true,
+            callbackEnabled = true,
+            noneable = true,
+            defaultValue = "None",
+            doc =
+                "<b>Experimental:</b> This API is in the process of being redesigned."
+                    + "<p>A schema for defining predeclared outputs. Unlike "
+                    + "<a href='attr.html#output'><code>output</code></a> and "
+                    + "<a href='attr.html#output_list'><code>output_list</code></a> attributes, "
+                    + "the user does not specify the labels for these files. "
+                    + "See the <a href='../rules.$DOC_EXT#files'>Rules page</a> for more on "
+                    + "predeclared outputs."
+                    + "<p>The value of this argument is either a dictionary or a callback function "
+                    + "that produces a dictionary. The callback works similar to computed "
+                    + "dependency attributes: The function's parameter names are matched against "
+                    + "the rule's attributes, so for example if you pass "
+                    + "<code>outputs = _my_func</code> with the definition "
+                    + "<code>def _my_func(srcs, deps): ...</code>, the function has access "
+                    + "to the attributes <code>srcs</code> and <code>deps</code>. Whether the "
+                    + "dictionary is specified directly or via a function, it is interpreted as "
+                    + "follows."
+                    + "<p>Each entry in the dictionary creates a predeclared output where the key "
+                    + "is an identifier and the value is a string template that determines the "
+                    + "output's label. In the rule's implementation function, the identifier "
+                    + "becomes the field name used to access the output's "
+                    + "<a href='File.html'><code>File</code></a> in "
+                    + "<a href='ctx.html#outputs'><code>ctx.outputs</code></a>. The output's label "
+                    + "has the same package as the rule, and the part after the package is "
+                    + "produced by substituting each placeholder of the form "
+                    + "<code>\"%{ATTR}\"</code> with a string formed from the value of the "
+                    + "attribute <code>ATTR</code>:"
+                    + "<ul>"
+                    + "<li>String-typed attributes are substituted verbatim."
+                    + "<li>Label-typed attributes become the part of the label after the package, "
+                    + "minus the file extension. For example, the label "
+                    + "<code>\"//pkg:a/b.c\"</code> becomes <code>\"a/b\"</code>."
+                    + "<li>Output-typed attributes become the part of the label after the package, "
+                    + "including the file extension (for the above example, "
+                    + "<code>\"a/b.c\"</code>)."
+                    + "<li>All list-typed attributes (for example, <code>attr.label_list</code>) "
+                    + "used in placeholders are required to have <i>exactly one element</i>. Their "
+                    + "conversion is the same as their non-list version (<code>attr.label</code>)."
+                    + "<li>Other attribute types may not appear in placeholders."
+                    + "<li>The special non-attribute placeholders <code>%{dirname}</code> and "
+                    + "<code>%{basename}</code> expand to those parts of the rule's label, "
+                    + "excluding its package. For example, in <code>\"//pkg:a/b.c\"</code>, the "
+                    + "dirname is <code>a</code> and the basename is <code>b.c</code>."
+                    + "</ul>"
+                    + "<p>In practice, the most common substitution placeholder is "
+                    + "<code>\"%{name}\"</code>. For example, for a target named \"foo\", the "
+                    + "outputs dict <code>{\"bin\": \"%{name}.exe\"}</code> predeclares an output "
+                    + "named <code>foo.exe</code> that is accessible in the implementation "
+                    + "function as <code>ctx.outputs.bin</code>."),
+        @Param(
+            name = "executable",
+            type = Boolean.class,
+            legacyNamed = true,
+            defaultValue = "False",
+            doc =
+                "Whether this rule is considered executable, that is, whether it may be the "
+                    + "subject of a <code>blaze run</code> command. See the "
+                    + "<a href='../rules.$DOC_EXT#executable-rules-and-test-rules'>Rules page</a> "
+                    + "for more information."),
+        @Param(
+            name = "output_to_genfiles",
+            type = Boolean.class,
+            legacyNamed = true,
+            defaultValue = "False",
+            doc =
+                "If true, the files will be generated in the genfiles directory instead of the "
+                    + "bin directory. Unless you need it for compatibility with existing rules "
+                    + "(e.g. when generating header files for C++), do not set this flag."),
+        @Param(
+            name = "fragments",
+            type = SkylarkList.class,
+            legacyNamed = true,
+            generic1 = String.class,
+            defaultValue = "[]",
+            doc =
+                "List of names of configuration fragments that the rule requires "
+                    + "in target configuration."),
+        @Param(
+            name = "host_fragments",
+            type = SkylarkList.class,
+            legacyNamed = true,
+            generic1 = String.class,
+            defaultValue = "[]",
+            doc =
+                "List of names of configuration fragments that the rule requires "
+                    + "in host configuration."),
+        @Param(
+            name = "_skylark_testable",
+            type = Boolean.class,
+            legacyNamed = true,
+            defaultValue = "False",
+            doc =
+                "<i>(Experimental)</i><br/><br/>"
+                    + "If true, this rule will expose its actions for inspection by rules that "
+                    + "depend on it via an <a href=\"globals.html#Actions\">Actions</a> "
+                    + "provider. The provider is also available to the rule itself by calling "
+                    + "<a href=\"ctx.html#created_actions\">ctx.created_actions()</a>."
+                    + "<br/><br/>"
+                    + "This should only be used for testing the analysis-time behavior of "
+                    + "Skylark rules. This flag may be removed in the future."),
+        @Param(
+            name = "toolchains",
+            type = SkylarkList.class,
+            legacyNamed = true,
+            generic1 = String.class,
+            defaultValue = "[]",
+            doc =
+                "<i>(Experimental)</i><br/><br/>"
+                    + "If set, the set of toolchains this rule requires. Toolchains will be "
+                    + "found by checking the current platform, and provided to the rule "
+                    + "implementation via <code>ctx.toolchain</code>."),
+        @Param(
+            name = "doc",
+            type = String.class,
+            legacyNamed = true,
+            defaultValue = "''",
+            doc =
+                "A description of the rule that can be extracted by documentation generating "
+                    + "tools."),
+        @Param(
+            name = "provides",
+            type = SkylarkList.class,
+            named = true,
+            positional = false,
+            defaultValue = "[]",
+            doc = PROVIDES_DOC),
+        @Param(
+            name = "execution_platform_constraints_allowed",
+            type = Boolean.class,
+            named = true,
+            positional = false,
+            defaultValue = "False",
+            doc =
+                "If true, a special attribute named <code>exec_compatible_with</code> of "
+                    + "label-list type is added, which must not already exist in "
+                    + "<code>attrs</code>. Targets may use this attribute to specify additional "
+                    + "constraints on the execution platform beyond those given in the "
+                    + "<code>exec_compatible_with</code> argument to <code>rule()</code>."),
+        @Param(
+            name = "exec_compatible_with",
+            type = SkylarkList.class,
+            generic1 = String.class,
+            named = true,
+            positional = false,
+            defaultValue = "[]",
+            doc =
+                "A list of constraints on the execution platform that apply to all targets of "
+                    + "this rule type."),
+        @Param(
+            name = "analysis_test",
+            allowedTypes = {
+              @ParamType(type = Boolean.class),
+              @ParamType(type = UnboundMarker.class)
+            },
+            named = true,
+            positional = false,
+            // TODO(cparsons): Make the default false when this is no longer experimental.
+            defaultValue = "unbound",
+            // TODO(cparsons): Link to in-build testing documentation when it is available.
+            doc =
+                "<b>Experimental: This parameter is experimental and subject to change at any "
+                    + "time.</b><p> If true, then this rule is treated as an analysis test."),
+      },
+      useAst = true,
+      useEnvironment = true)
   public BaseFunction rule(
       BaseFunction implementation,
       Boolean test,
@@ -317,6 +325,7 @@
       SkylarkList<?> providesArg,
       Boolean executionPlatformConstraintsAllowed,
       SkylarkList<?> execCompatibleWith,
+      Object analysisTest,
       FuncallExpression ast,
       Environment funcallEnv)
       throws EvalException;
diff --git a/src/main/java/com/google/devtools/build/lib/skylarkbuildapi/java/JavaCommonApi.java b/src/main/java/com/google/devtools/build/lib/skylarkbuildapi/java/JavaCommonApi.java
index aa99ba5..802db05 100644
--- a/src/main/java/com/google/devtools/build/lib/skylarkbuildapi/java/JavaCommonApi.java
+++ b/src/main/java/com/google/devtools/build/lib/skylarkbuildapi/java/JavaCommonApi.java
@@ -367,12 +367,14 @@
             named = true,
             type = TransitiveInfoCollectionApi.class,
             doc = "A label pointing to a java_toolchain rule to used to find the ijar tool."),
-      })
+      },
+      useLocation = true)
   public FileApi runIjar(
       SkylarkActionFactoryT actions,
       FileT jar,
       Object targetLabel,
-      TransitiveInfoCollectionT javaToolchain)
+      TransitiveInfoCollectionT javaToolchain,
+      Location location)
       throws EvalException;
 
   @SkylarkCallable(
@@ -412,10 +414,14 @@
             named = true,
             type = TransitiveInfoCollectionApi.class,
             doc = "A label pointing to a java_toolchain rule to used to find the stamp_jar tool."),
-      })
+      },
+      useLocation = true)
   public FileApi stampJar(
-      SkylarkActionFactoryT actions, FileT jar, Label targetLabel,
-      TransitiveInfoCollectionT javaToolchain)
+      SkylarkActionFactoryT actions,
+      FileT jar,
+      Label targetLabel,
+      TransitiveInfoCollectionT javaToolchain,
+      Location location)
       throws EvalException;
 
   @SkylarkCallable(
@@ -466,14 +472,16 @@
             type = TransitiveInfoCollectionApi.class,
             doc = "A label pointing to a JDK to be used for packing sources."),
       },
-      allowReturnNones = true)
+      allowReturnNones = true,
+      useLocation = true)
   public FileApi packSources(
       SkylarkActionFactoryT actions,
       FileT outputJar,
       SkylarkList<FileT> sourceFiles,
       SkylarkList<FileT> sourceJars,
       TransitiveInfoCollectionT javaToolchain,
-      TransitiveInfoCollectionT hostJavabase)
+      TransitiveInfoCollectionT hostJavabase,
+      Location location)
       throws EvalException;
 
   @SkylarkCallable(
diff --git a/src/main/java/com/google/devtools/build/skydoc/fakebuildapi/FakeSkylarkRuleFunctionsApi.java b/src/main/java/com/google/devtools/build/skydoc/fakebuildapi/FakeSkylarkRuleFunctionsApi.java
index 1c3d82b..29a7cb2 100644
--- a/src/main/java/com/google/devtools/build/skydoc/fakebuildapi/FakeSkylarkRuleFunctionsApi.java
+++ b/src/main/java/com/google/devtools/build/skydoc/fakebuildapi/FakeSkylarkRuleFunctionsApi.java
@@ -101,12 +101,25 @@
   }
 
   @Override
-  public BaseFunction rule(BaseFunction implementation, Boolean test, Object attrs,
-      Object implicitOutputs, Boolean executable, Boolean outputToGenfiles,
-      SkylarkList<?> fragments, SkylarkList<?> hostFragments, Boolean skylarkTestable,
-      SkylarkList<?> toolchains, String doc, SkylarkList<?> providesArg,
-      Boolean executionPlatformConstraintsAllowed, SkylarkList<?> execCompatibleWith,
-      FuncallExpression ast, Environment funcallEnv) throws EvalException {
+  public BaseFunction rule(
+      BaseFunction implementation,
+      Boolean test,
+      Object attrs,
+      Object implicitOutputs,
+      Boolean executable,
+      Boolean outputToGenfiles,
+      SkylarkList<?> fragments,
+      SkylarkList<?> hostFragments,
+      Boolean skylarkTestable,
+      SkylarkList<?> toolchains,
+      String doc,
+      SkylarkList<?> providesArg,
+      Boolean executionPlatformConstraintsAllowed,
+      SkylarkList<?> execCompatibleWith,
+      Object analysisTest,
+      FuncallExpression ast,
+      Environment funcallEnv)
+      throws EvalException {
     List<AttributeInfo> attrInfos;
     ImmutableMap.Builder<String, FakeDescriptor> attrsMapBuilder = ImmutableMap.builder();
     if (attrs != null && attrs != Runtime.NONE) {
diff --git a/src/main/java/com/google/devtools/build/skydoc/fakebuildapi/java/FakeJavaCommon.java b/src/main/java/com/google/devtools/build/skydoc/fakebuildapi/java/FakeJavaCommon.java
index 8ca3718..c673f2b 100644
--- a/src/main/java/com/google/devtools/build/skydoc/fakebuildapi/java/FakeJavaCommon.java
+++ b/src/main/java/com/google/devtools/build/skydoc/fakebuildapi/java/FakeJavaCommon.java
@@ -61,21 +61,36 @@
   }
 
   @Override
-  public FileApi runIjar(SkylarkActionFactoryApi actions, FileApi jar, Object targetLabel,
-      TransitiveInfoCollectionApi javaToolchain) throws EvalException {
+  public FileApi runIjar(
+      SkylarkActionFactoryApi actions,
+      FileApi jar,
+      Object targetLabel,
+      TransitiveInfoCollectionApi javaToolchain,
+      Location location)
+      throws EvalException {
     return null;
   }
 
   @Override
-  public FileApi stampJar(SkylarkActionFactoryApi actions, FileApi jar, Label targetLabel,
-      TransitiveInfoCollectionApi javaToolchain) throws EvalException {
+  public FileApi stampJar(
+      SkylarkActionFactoryApi actions,
+      FileApi jar,
+      Label targetLabel,
+      TransitiveInfoCollectionApi javaToolchain,
+      Location location)
+      throws EvalException {
     return null;
   }
 
   @Override
-  public FileApi packSources(SkylarkActionFactoryApi actions, FileApi outputJar,
-      SkylarkList<FileApi> sourceFiles, SkylarkList<FileApi> sourceJars,
-      TransitiveInfoCollectionApi javaToolchain, TransitiveInfoCollectionApi hostJavabase)
+  public FileApi packSources(
+      SkylarkActionFactoryApi actions,
+      FileApi outputJar,
+      SkylarkList<FileApi> sourceFiles,
+      SkylarkList<FileApi> sourceJars,
+      TransitiveInfoCollectionApi javaToolchain,
+      TransitiveInfoCollectionApi hostJavabase,
+      Location location)
       throws EvalException {
     return null;
   }