Allow passing location, ast, and environment to @SkylarkCallable methods

RELNOTES: None.
PiperOrigin-RevId: 188201686
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/FuncallExpression.java b/src/main/java/com/google/devtools/build/lib/syntax/FuncallExpression.java
index 2639c27..841a94a 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/FuncallExpression.java
+++ b/src/main/java/com/google/devtools/build/lib/syntax/FuncallExpression.java
@@ -261,18 +261,18 @@
     return numPositionalArgs;
   }
 
-   @Override
-   public void prettyPrint(Appendable buffer) throws IOException {
-     function.prettyPrint(buffer);
-     buffer.append('(');
-     String sep = "";
-     for (Argument.Passed arg : arguments) {
-       buffer.append(sep);
-       arg.prettyPrint(buffer);
-       sep = ", ";
-     }
-     buffer.append(')');
-   }
+  @Override
+  public void prettyPrint(Appendable buffer) throws IOException {
+    function.prettyPrint(buffer);
+    buffer.append('(');
+    String sep = "";
+    for (Argument.Passed arg : arguments) {
+      buffer.append(sep);
+      arg.prettyPrint(buffer);
+      sep = ", ";
+    }
+    buffer.append(')');
+  }
 
   @Override
   public String toString() {
@@ -361,7 +361,7 @@
               "method invocation returned None, please file a bug report: "
                   + methodName
                   + Printer.printAbbreviatedList(
-                      ImmutableList.copyOf(args), "(", ", ", ")", null));
+                  ImmutableList.copyOf(args), "(", ", ", ")", null));
         }
       }
       // TODO(bazel-team): get rid of this, by having everyone use the Skylark data structures
@@ -394,7 +394,11 @@
   // exactly and copy that behaviour.
   // Throws an EvalException when it cannot find a matching function.
   private Pair<MethodDescriptor, List<Object>> findJavaMethod(
-      Class<?> objClass, String methodName, List<Object> args, Map<String, Object> kwargs)
+      Class<?> objClass,
+      String methodName,
+      List<Object> args,
+      Map<String, Object> kwargs,
+      Environment environment)
       throws EvalException {
     Pair<MethodDescriptor, List<Object>> matchingMethod = null;
     List<MethodDescriptor> methods = getMethods(objClass, methodName);
@@ -402,9 +406,10 @@
     if (methods != null) {
       for (MethodDescriptor method : methods) {
         if (method.getAnnotation().structField()) {
+          // TODO(cparsons): Allow structField methods to accept interpreter-supplied arguments.
           return new Pair<>(method, null);
         } else {
-          argumentListConversionResult = convertArgumentList(args, kwargs, method);
+          argumentListConversionResult = convertArgumentList(args, kwargs, method, environment);
           if (argumentListConversionResult.getArguments() != null) {
             if (matchingMethod == null) {
               matchingMethod = new Pair<>(method, argumentListConversionResult.getArguments());
@@ -471,26 +476,36 @@
    * any. If there is a type or argument mismatch, returns a result containing an error message.
    */
   private ArgumentListConversionResult convertArgumentList(
-      List<Object> args, Map<String, Object> kwargs, MethodDescriptor method) {
+      List<Object> args,
+      Map<String, Object> kwargs,
+      MethodDescriptor method,
+      Environment environment) {
     ImmutableList.Builder<Object> builder = ImmutableList.builder();
-    Class<?>[] params = method.getMethod().getParameterTypes();
+    Class<?>[] javaMethodSignatureParams = method.getMethod().getParameterTypes();
     SkylarkCallable callable = method.getAnnotation();
+    int numExtraInterpreterParams = 0;
+    numExtraInterpreterParams += callable.useLocation() ? 1 : 0;
+    numExtraInterpreterParams += callable.useAst() ? 1 : 0;
+    numExtraInterpreterParams += callable.useEnvironment() ? 1 : 0;
+
     int mandatoryPositionals = callable.mandatoryPositionals();
     if (mandatoryPositionals < 0) {
       if (callable.parameters().length > 0) {
         mandatoryPositionals = 0;
       } else {
-        mandatoryPositionals = params.length;
+        mandatoryPositionals = javaMethodSignatureParams.length - numExtraInterpreterParams;
       }
     }
-    if (mandatoryPositionals > args.size()
-        || args.size() > mandatoryPositionals + callable.parameters().length) {
+    if (mandatoryPositionals > args.size()) {
+      return ArgumentListConversionResult.fromError("too few arguments");
+    }
+    if (args.size() > mandatoryPositionals + callable.parameters().length) {
       return ArgumentListConversionResult.fromError("too many arguments");
     }
     // First process the legacy positional parameters.
     int i = 0;
     if (mandatoryPositionals > 0) {
-      for (Class<?> param : params) {
+      for (Class<?> param : javaMethodSignatureParams) {
         Object value = args.get(i);
         if (!param.isAssignableFrom(value.getClass())) {
           return ArgumentListConversionResult.fromError(
@@ -500,7 +515,7 @@
         }
         builder.add(value);
         i++;
-        if (mandatoryPositionals >= 0 && i >= mandatoryPositionals) {
+        if (i >= mandatoryPositionals) {
           // Stops for specified parameters instead.
           break;
         }
@@ -554,9 +569,21 @@
     if (!keys.isEmpty()) {
       return ArgumentListConversionResult.fromError(
           String.format("unexpected keyword%s %s",
-          keys.size() > 1 ? "s" : "",
-          Joiner.on(",").join(Iterables.transform(keys, s -> "'" + s + "'"))));
+              keys.size() > 1 ? "s" : "",
+              Joiner.on(",").join(Iterables.transform(keys, s -> "'" + s + "'"))));
     }
+
+    // Then add any skylark-info arguments (for example the Environment).
+    if (callable.useLocation()) {
+      builder.add(getLocation());
+    }
+    if (callable.useAst()) {
+      builder.add(this);
+    }
+    if (callable.useEnvironment()) {
+      builder.add(environment);
+    }
+
     return ArgumentListConversionResult.fromArgumentList(builder.build());
   }
 
@@ -690,7 +717,7 @@
         objClass = value.getClass();
       }
       Pair<MethodDescriptor, List<Object>> javaMethod =
-          call.findJavaMethod(objClass, method, positionalArgs, keyWordArgs);
+          call.findJavaMethod(objClass, method, positionalArgs, keyWordArgs, env);
       if (javaMethod.first.getAnnotation().structField()) {
         // Not a method but a callable attribute
         try {