Migrate SkylarkBuiltin annotations to SkylarkSignature instead.

--
MOS_MIGRATED_REVID=91603959
diff --git a/src/main/java/com/google/devtools/build/docgen/SkylarkDocumentationProcessor.java b/src/main/java/com/google/devtools/build/docgen/SkylarkDocumentationProcessor.java
index d27e71d..e3f6df2 100644
--- a/src/main/java/com/google/devtools/build/docgen/SkylarkDocumentationProcessor.java
+++ b/src/main/java/com/google/devtools/build/docgen/SkylarkDocumentationProcessor.java
@@ -27,13 +27,17 @@
 import com.google.devtools.build.lib.packages.MethodLibrary;
 import com.google.devtools.build.lib.rules.SkylarkModules;
 import com.google.devtools.build.lib.rules.SkylarkRuleContext;
+import com.google.devtools.build.lib.syntax.BaseFunction;
 import com.google.devtools.build.lib.syntax.Environment;
 import com.google.devtools.build.lib.syntax.Environment.NoneType;
 import com.google.devtools.build.lib.syntax.EvalUtils;
-import com.google.devtools.build.lib.syntax.SkylarkBuiltin;
-import com.google.devtools.build.lib.syntax.SkylarkBuiltin.Param;
+import com.google.devtools.build.lib.syntax.FuncallExpression;
 import com.google.devtools.build.lib.syntax.SkylarkCallable;
+import com.google.devtools.build.lib.syntax.SkylarkList;
 import com.google.devtools.build.lib.syntax.SkylarkModule;
+import com.google.devtools.build.lib.syntax.SkylarkSignature;
+import com.google.devtools.build.lib.syntax.SkylarkSignature.Param;
+import com.google.devtools.build.lib.syntax.SkylarkSignatureProcessor.HackHackEitherList;
 
 import java.io.BufferedWriter;
 import java.io.File;
@@ -42,6 +46,7 @@
 import java.lang.reflect.Method;
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -163,7 +168,7 @@
 
   private void generateBuiltinItemDoc(
       String moduleId, SkylarkBuiltinMethod method, StringBuilder sb) {
-    SkylarkBuiltin annotation = method.annotation;
+    SkylarkSignature annotation = method.annotation;
     if (!annotation.documented()) {
       return;
     }
@@ -172,7 +177,7 @@
           annotation.name(),
           annotation.name()));
 
-    if (com.google.devtools.build.lib.syntax.Function.class.isAssignableFrom(method.fieldClass)) {
+    if (BaseFunction.class.isAssignableFrom(method.fieldClass)) {
       sb.append(getSignature(moduleId, annotation));
     } else {
       if (!annotation.returnType().equals(Object.class)) {
@@ -184,11 +189,36 @@
     printParams(moduleId, annotation, sb);
   }
 
-  private void printParams(String moduleId, SkylarkBuiltin annotation, StringBuilder sb) {
-    if (annotation.mandatoryParams().length + annotation.optionalParams().length > 0) {
+  // Elide self parameter from mandatoryPositionals in class methods.
+  private static Param[] adjustedMandatoryPositionals(SkylarkSignature annotation) {
+    Param[] mandatoryPos = annotation.mandatoryPositionals();
+    if (mandatoryPos.length > 0
+        && annotation.objectType() != Object.class
+        && !FuncallExpression.isNamespace(annotation.objectType())) {
+      // Skip the self parameter, which is the first mandatory positional parameter.
+      return Arrays.copyOfRange(mandatoryPos, 1, mandatoryPos.length);
+    } else {
+      return mandatoryPos;
+    }
+  }
+
+  private void printParams(String moduleId, SkylarkSignature annotation, StringBuilder sb) {
+    Param[] mandatoryPos = adjustedMandatoryPositionals(annotation);
+    Param[] optionalPos = annotation.optionalPositionals();
+    Param[] optionalKey = annotation.optionalNamedOnly();
+    Param[] mandatoryKey = annotation.mandatoryNamedOnly();
+    Param[] star = annotation.extraPositionals();
+    Param[] starStar = annotation.extraKeywords();
+
+    if (mandatoryPos.length + optionalPos.length + optionalKey.length + mandatoryKey.length
+        + star.length + starStar.length > 0) {
       sb.append("<h4>Parameters</h4>\n");
-      printParams(moduleId, annotation.name(), annotation.mandatoryParams(), sb);
-      printParams(moduleId, annotation.name(), annotation.optionalParams(), sb);
+      printParams(moduleId, annotation.name(), mandatoryPos, sb);
+      printParams(moduleId, annotation.name(), optionalPos, sb);
+      printParams(moduleId, annotation.name(), star, sb);
+      printParams(moduleId, annotation.name(), mandatoryKey, sb);
+      printParams(moduleId, annotation.name(), optionalKey, sb);
+      printParams(moduleId, annotation.name(), starStar, sb);
     } else {
       sb.append("<br/>\n");
     }
@@ -229,13 +259,29 @@
         getTypeAnchor(method.getReturnType()), objectName, methodName, args);
   }
 
-  private String getSignature(String objectName, SkylarkBuiltin method) {
+  private String getSignature(String objectName, SkylarkSignature method) {
     List<String> argList = new ArrayList<>();
-    for (Param param : method.mandatoryParams()) {
+    for (Param param : adjustedMandatoryPositionals(method)) {
       argList.add(param.name());
     }
-    for (Param param : method.optionalParams()) {
-      argList.add(param.name() + "?");
+    for (Param param : method.optionalPositionals()) {
+      argList.add(param.name() + "?"); // or should we use pythonic " = &#ellipsis;" instead?
+    }
+    for (Param param : method.extraPositionals()) {
+      argList.add("*" + param.name());
+    }
+    if (method.extraPositionals().length == 0
+        && (method.optionalNamedOnly().length > 0 || method.mandatoryNamedOnly().length > 0)) {
+      argList.add("*");
+    }
+    for (Param param : method.mandatoryNamedOnly()) {
+      argList.add(param.name());
+    }
+    for (Param param : method.optionalNamedOnly()) {
+      argList.add(param.name() + "?"); // or should we be more pythonic with this? " = ..."
+    }
+    for (Param param : method.extraKeywords()) {
+      argList.add("**" + param.name());
     }
     String args = "(" + Joiner.on(", ").join(argList) + ")";
     if (!objectName.equals(TOP_LEVEL_ID)) {
@@ -258,7 +304,8 @@
       return "<a class=\"anchor\" href=\"#modules.string\">string</a>";
     } else if (Map.class.isAssignableFrom(type)) {
       return "<a class=\"anchor\" href=\"#modules.dict\">dict</a>";
-    } else if (List.class.isAssignableFrom(type)) {
+    } else if (List.class.isAssignableFrom(type) || SkylarkList.class.isAssignableFrom(type)
+        || type == HackHackEitherList.class) {
       // Annotated Java methods can return simple java.util.Lists (which get auto-converted).
       return "<a class=\"anchor\" href=\"#modules.list\">list</a>";
     } else if (type.equals(Void.TYPE) || type.equals(NoneType.class)) {
@@ -405,7 +452,7 @@
   }
 
   private void printBuiltinFunctionDoc(
-      String moduleName, SkylarkBuiltin annotation, StringBuilder sb) {
+      String moduleName, SkylarkSignature annotation, StringBuilder sb) {
     if (moduleName != null) {
       sb.append(moduleName).append(".");
     }
@@ -427,9 +474,9 @@
 
   private void collectBuiltinDoc(Map<String, SkylarkModuleDoc> modules, Field[] fields) {
     for (Field field : fields) {
-      if (field.isAnnotationPresent(SkylarkBuiltin.class)) {
-        SkylarkBuiltin skylarkBuiltin = field.getAnnotation(SkylarkBuiltin.class);
-        Class<?> moduleClass = skylarkBuiltin.objectType();
+      if (field.isAnnotationPresent(SkylarkSignature.class)) {
+        SkylarkSignature skylarkSignature = field.getAnnotation(SkylarkSignature.class);
+        Class<?> moduleClass = skylarkSignature.objectType();
         SkylarkModule skylarkModule = moduleClass.equals(Object.class)
             ? getTopLevelModule()
             : moduleClass.getAnnotation(SkylarkModule.class);
@@ -437,7 +484,8 @@
           modules.put(skylarkModule.name(), new SkylarkModuleDoc(skylarkModule, moduleClass));
         }
         modules.get(skylarkModule.name()).getBuiltinMethods()
-            .put(skylarkBuiltin.name(), new SkylarkBuiltinMethod(skylarkBuiltin, field.getType()));
+            .put(skylarkSignature.name(),
+                new SkylarkBuiltinMethod(skylarkSignature, field.getType()));
       }
     }
   }
diff --git a/src/main/java/com/google/devtools/build/docgen/SkylarkJavaInterfaceExplorer.java b/src/main/java/com/google/devtools/build/docgen/SkylarkJavaInterfaceExplorer.java
index 8f58f71..1118991 100644
--- a/src/main/java/com/google/devtools/build/docgen/SkylarkJavaInterfaceExplorer.java
+++ b/src/main/java/com/google/devtools/build/docgen/SkylarkJavaInterfaceExplorer.java
@@ -16,9 +16,9 @@
 import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableMap;
 import com.google.devtools.build.lib.syntax.FuncallExpression;
-import com.google.devtools.build.lib.syntax.SkylarkBuiltin;
 import com.google.devtools.build.lib.syntax.SkylarkCallable;
 import com.google.devtools.build.lib.syntax.SkylarkModule;
+import com.google.devtools.build.lib.syntax.SkylarkSignature;
 import com.google.devtools.build.lib.util.StringUtilities;
 
 import java.lang.reflect.Method;
@@ -59,17 +59,17 @@
    * A class representing a Skylark built-in object or method.
    */
   static final class SkylarkBuiltinMethod {
-    public final SkylarkBuiltin annotation;
+    public final SkylarkSignature annotation;
     public final Class<?> fieldClass;
 
-    public SkylarkBuiltinMethod(SkylarkBuiltin annotation, Class<?> fieldClass) {
+    public SkylarkBuiltinMethod(SkylarkSignature annotation, Class<?> fieldClass) {
       this.annotation = annotation;
       this.fieldClass = fieldClass;
     }
   }
 
   /**
-   * A class representing a Skylark built-in object with its {@link SkylarkBuiltin} annotation
+   * A class representing a Skylark built-in object with its {@link SkylarkSignature} annotation
    * and the {@link SkylarkCallable} methods it might have.
    */
   static final class SkylarkModuleDoc {
@@ -80,8 +80,8 @@
     private ArrayList<SkylarkJavaMethod> methods = null;
 
     SkylarkModuleDoc(SkylarkModule module, Class<?> classObject) {
-      this.module = Preconditions.checkNotNull(module,
-          "Class has to be annotated with SkylarkModule: " + classObject);
+      this.module = Preconditions.checkNotNull(
+          module, "Class has to be annotated with SkylarkModule: %s", classObject);
       this.classObject = classObject;
       this.builtin = new TreeMap<>();
     }
@@ -113,9 +113,9 @@
 
   /**
    * Collects and returns all the Java objects reachable in Skylark from (and including)
-   * firstClassObject with the corresponding SkylarkBuiltin annotations.
+   * firstClassObject with the corresponding SkylarkSignature annotations.
    *
-   * <p>Note that the {@link SkylarkBuiltin} annotation for firstClassObject - firstAnnotation -
+   * <p>Note that the {@link SkylarkSignature} annotation for firstClassObject - firstAnnotation -
    * is also an input parameter, because some top level Skylark built-in objects and methods
    * are not annotated on the class, but on a field referencing them.
    */
diff --git a/src/main/java/com/google/devtools/build/lib/packages/MethodLibrary.java b/src/main/java/com/google/devtools/build/lib/packages/MethodLibrary.java
index 82a2f0a..9466c69 100644
--- a/src/main/java/com/google/devtools/build/lib/packages/MethodLibrary.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/MethodLibrary.java
@@ -38,12 +38,13 @@
 import com.google.devtools.build.lib.syntax.MixedModeFunction;
 import com.google.devtools.build.lib.syntax.SelectorList;
 import com.google.devtools.build.lib.syntax.SelectorValue;
-import com.google.devtools.build.lib.syntax.SkylarkBuiltin;
-import com.google.devtools.build.lib.syntax.SkylarkBuiltin.Param;
 import com.google.devtools.build.lib.syntax.SkylarkEnvironment;
 import com.google.devtools.build.lib.syntax.SkylarkList;
 import com.google.devtools.build.lib.syntax.SkylarkModule;
 import com.google.devtools.build.lib.syntax.SkylarkNestedSet;
+import com.google.devtools.build.lib.syntax.SkylarkSignature;
+import com.google.devtools.build.lib.syntax.SkylarkSignature.Param;
+import com.google.devtools.build.lib.syntax.SkylarkSignatureProcessor.HackHackEitherList;
 
 import java.util.ArrayList;
 import java.util.Collection;
@@ -65,6 +66,16 @@
 
   private MethodLibrary() {}
 
+  // TODO(bazel-team):
+  // the Build language and Skylark currently have different list types:
+  // the Build language uses plain java List (usually ArrayList) which is mutable and accepts
+  // any argument, whereas Skylark uses SkylarkList which is immutable and accepts only
+  // arguments of the same kind. Some methods below use HackHackEitherList as a magic marker
+  // to indicate that either kind of lists is used depending on the evaluation context.
+  // It might be a good idea to either have separate methods for the two languages where it matters,
+  // or to unify the two languages so they use the same data structure (which might require
+  // updating all existing clients).
+
   // Convert string index in the same way Python does.
   // If index is negative, starts from the end.
   // If index is outside bounds, it is restricted to the valid range.
@@ -109,14 +120,15 @@
     return index;
   }
 
-    // supported string methods
+  // supported string methods
 
-  @SkylarkBuiltin(name = "join", objectType = StringModule.class, returnType = String.class,
+  @SkylarkSignature(name = "join", objectType = StringModule.class, returnType = String.class,
       doc = "Returns a string in which the string elements of the argument have been "
           + "joined by this string as a separator. Example:<br>"
           + "<pre class=\"language-python\">\"|\".join([\"a\", \"b\", \"c\"]) == \"a|b|c\"</pre>",
-      mandatoryParams = {
-      @Param(name = "elements", type = SkylarkList.class, doc = "The objects to join.")})
+      mandatoryPositionals = {
+        @Param(name = "self", type = String.class, doc = "This string, a separator."),
+        @Param(name = "elements", type = HackHackEitherList.class, doc = "The objects to join.")})
   private static Function join = new MixedModeFunction("join",
       ImmutableList.of("this", "elements"), 2, false) {
     @Override
@@ -126,9 +138,11 @@
       return Joiner.on(thisString).join(seq);
     }};
 
-  @SkylarkBuiltin(name = "lower", objectType = StringModule.class, returnType = String.class,
-      doc = "Returns the lower case version of this string.")
-      private static Function lower = new MixedModeFunction("lower",
+  @SkylarkSignature(name = "lower", objectType = StringModule.class, returnType = String.class,
+      doc = "Returns the lower case version of this string.",
+      mandatoryPositionals = {
+        @Param(name = "self", type = String.class, doc = "This string, to convert to lower case.")})
+  private static Function lower = new MixedModeFunction("lower",
           ImmutableList.of("this"), 1, false) {
     @Override
     public Object call(Object[] args, FuncallExpression ast) throws ConversionException {
@@ -137,8 +151,10 @@
     }
   };
 
-  @SkylarkBuiltin(name = "upper", objectType = StringModule.class, returnType = String.class,
-      doc = "Returns the upper case version of this string.")
+  @SkylarkSignature(name = "upper", objectType = StringModule.class, returnType = String.class,
+      doc = "Returns the upper case version of this string.",
+      mandatoryPositionals = {
+        @Param(name = "self", type = String.class, doc = "This string, to convert to upper case.")})
     private static Function upper = new MixedModeFunction("upper",
         ImmutableList.of("this"), 1, false) {
     @Override
@@ -148,15 +164,18 @@
     }
   };
 
-  @SkylarkBuiltin(name = "replace", objectType = StringModule.class, returnType = String.class,
+  @SkylarkSignature(name = "replace", objectType = StringModule.class, returnType = String.class,
       doc = "Returns a copy of the string in which the occurrences "
           + "of <code>old</code> have been replaced with <code>new</code>, optionally restricting "
           + "the number of replacements to <code>maxsplit</code>.",
-      mandatoryParams = {
-      @Param(name = "old", type = String.class, doc = "The string to be replaced."),
-      @Param(name = "new", type = String.class, doc = "The string to replace with.")},
-      optionalParams = {
-      @Param(name = "maxsplit", type = Integer.class, doc = "The maximum number of replacements.")})
+      mandatoryPositionals = {
+        @Param(name = "self", type = String.class, doc = "This string."),
+        @Param(name = "old", type = String.class, doc = "The string to be replaced."),
+        @Param(name = "new", type = String.class, doc = "The string to replace with.")},
+      optionalPositionals = {
+        @Param(name = "maxsplit", type = Integer.class,
+            doc = "The maximum number of replacements.")},
+      useLocation = true)
   private static Function replace =
     new MixedModeFunction("replace", ImmutableList.of("this", "old", "new", "maxsplit"), 3, false) {
     @Override
@@ -182,13 +201,17 @@
     }
   };
 
-  @SkylarkBuiltin(name = "split", objectType = StringModule.class, returnType = SkylarkList.class,
+  @SkylarkSignature(name = "split", objectType = StringModule.class,
+      returnType = HackHackEitherList.class,
       doc = "Returns a list of all the words in the string, using <code>sep</code>  "
           + "as the separator, optionally limiting the number of splits to <code>maxsplit</code>.",
-      optionalParams = {
-      @Param(name = "sep", type = String.class,
-          doc = "The string to split on, default is space (\" \")."),
-      @Param(name = "maxsplit", type = Integer.class, doc = "The maximum number of splits.")})
+      mandatoryPositionals = {
+        @Param(name = "self", type = String.class, doc = "This string.")},
+      optionalPositionals = {
+        @Param(name = "sep", type = String.class,
+            doc = "The string to split on, default is space (\" \")."),
+        @Param(name = "maxsplit", type = Integer.class, doc = "The maximum number of splits.")},
+      useEnvironment = true)
   private static Function split = new MixedModeFunction("split",
       ImmutableList.of("this", "sep", "maxsplit"), 1, false) {
     @Override
@@ -233,16 +256,19 @@
     return stringFind(forward, thiz, sub, start, args[3], functionName + " argument");
   }
 
-  @SkylarkBuiltin(name = "rfind", objectType = StringModule.class, returnType = Integer.class,
+  @SkylarkSignature(name = "rfind", objectType = StringModule.class, returnType = Integer.class,
       doc = "Returns the last index where <code>sub</code> is found, "
           + "or -1 if no such index exists, optionally restricting to "
           + "[<code>start</code>:<code>end</code>], "
           + "<code>start</code> being inclusive and <code>end</code> being exclusive.",
-      mandatoryParams = {
-      @Param(name = "sub", type = String.class, doc = "The substring to find.")},
-      optionalParams = {
-      @Param(name = "start", type = Integer.class, doc = "Restrict to search from this position."),
-      @Param(name = "end", type = Integer.class, doc = "Restrict to search before this position.")})
+      mandatoryPositionals = {
+        @Param(name = "self", type = String.class, doc = "This string."),
+        @Param(name = "sub", type = String.class, doc = "The substring to find.")},
+      optionalPositionals = {
+        @Param(name = "start", type = Integer.class, defaultValue = "0",
+            doc = "Restrict to search from this position."),
+        @Param(name = "end", type = Integer.class, noneable = true, defaultValue = "None",
+            doc = "optional position before which to restrict to search.")})
   private static Function rfind =
       new MixedModeFunction("rfind", ImmutableList.of("this", "sub", "start", "end"), 2, false) {
         @Override
@@ -251,16 +277,19 @@
         }
       };
 
-  @SkylarkBuiltin(name = "find", objectType = StringModule.class, returnType = Integer.class,
+  @SkylarkSignature(name = "find", objectType = StringModule.class, returnType = Integer.class,
       doc = "Returns the first index where <code>sub</code> is found, "
           + "or -1 if no such index exists, optionally restricting to "
           + "[<code>start</code>:<code>end]</code>, "
           + "<code>start</code> being inclusive and <code>end</code> being exclusive.",
-      mandatoryParams = {
-      @Param(name = "sub", type = String.class, doc = "The substring to find.")},
-      optionalParams = {
-      @Param(name = "start", type = Integer.class, doc = "Restrict to search from this position."),
-      @Param(name = "end", type = Integer.class, doc = "Restrict to search before this position.")})
+      mandatoryPositionals = {
+        @Param(name = "self", type = String.class, doc = "This string."),
+        @Param(name = "sub", type = String.class, doc = "The substring to find.")},
+      optionalPositionals = {
+        @Param(name = "start", type = Integer.class, defaultValue = "0",
+            doc = "Restrict to search from this position."),
+        @Param(name = "end", type = Integer.class, noneable = true, defaultValue = "None",
+            doc = "optional position before which to restrict to search.")})
   private static Function find =
       new MixedModeFunction("find", ImmutableList.of("this", "sub", "start", "end"), 2, false) {
         @Override
@@ -270,16 +299,20 @@
         }
       };
 
-  @SkylarkBuiltin(name = "rindex", objectType = StringModule.class, returnType = Integer.class,
+  @SkylarkSignature(name = "rindex", objectType = StringModule.class, returnType = Integer.class,
       doc = "Returns the last index where <code>sub</code> is found, "
           + "or throw an error if no such index exists, optionally restricting to "
           + "[<code>start</code>:<code>end</code>], "
           + "<code>start</code> being inclusive and <code>end</code> being exclusive.",
-      mandatoryParams = {
-      @Param(name = "sub", type = String.class, doc = "The substring to find.")},
-      optionalParams = {
-      @Param(name = "start", type = Integer.class, doc = "Restrict to search from this position."),
-      @Param(name = "end", type = Integer.class, doc = "Restrict to search before this position.")})
+      mandatoryPositionals = {
+        @Param(name = "self", type = String.class, doc = "This string."),
+        @Param(name = "sub", type = String.class, doc = "The substring to find.")},
+      optionalPositionals = {
+        @Param(name = "start", type = Integer.class, defaultValue = "0",
+            doc = "Restrict to search from this position."),
+        @Param(name = "end", type = Integer.class, noneable = true, defaultValue = "None",
+            doc = "optional position before which to restrict to search.")},
+      useLocation = true)
   private static Function rindex =
       new MixedModeFunction("rindex", ImmutableList.of("this", "sub", "start", "end"), 2, false) {
         @Override
@@ -295,16 +328,20 @@
         }
       };
 
-  @SkylarkBuiltin(name = "index", objectType = StringModule.class, returnType = Integer.class,
+  @SkylarkSignature(name = "index", objectType = StringModule.class, returnType = Integer.class,
       doc = "Returns the first index where <code>sub</code> is found, "
           + "or throw an error if no such index exists, optionally restricting to "
           + "[<code>start</code>:<code>end]</code>, "
           + "<code>start</code> being inclusive and <code>end</code> being exclusive.",
-      mandatoryParams = {
-      @Param(name = "sub", type = String.class, doc = "The substring to find.")},
-      optionalParams = {
-      @Param(name = "start", type = Integer.class, doc = "Restrict to search from this position."),
-      @Param(name = "end", type = Integer.class, doc = "Restrict to search before this position.")})
+      mandatoryPositionals = {
+        @Param(name = "self", type = String.class, doc = "This string."),
+        @Param(name = "sub", type = String.class, doc = "The substring to find.")},
+      optionalPositionals = {
+        @Param(name = "start", type = Integer.class, defaultValue = "0",
+            doc = "Restrict to search from this position."),
+        @Param(name = "end", type = Integer.class, noneable = true,
+            doc = "optional position before which to restrict to search.")},
+      useLocation = true)
   private static Function index =
       new MixedModeFunction("index", ImmutableList.of("this", "sub", "start", "end"), 2, false) {
         @Override
@@ -320,15 +357,18 @@
         }
       };
 
-  @SkylarkBuiltin(name = "count", objectType = StringModule.class, returnType = Integer.class,
+  @SkylarkSignature(name = "count", objectType = StringModule.class, returnType = Integer.class,
       doc = "Returns the number of (non-overlapping) occurrences of substring <code>sub</code> in "
           + "string, optionally restricting to [<code>start</code>:<code>end</code>], "
           + "<code>start</code> being inclusive and <code>end</code> being exclusive.",
-      mandatoryParams = {
-      @Param(name = "sub", type = String.class, doc = "The substring to count.")},
-      optionalParams = {
-      @Param(name = "start", type = Integer.class, doc = "Restrict to search from this position."),
-      @Param(name = "end", type = Integer.class, doc = "Restrict to search before this position.")})
+      mandatoryPositionals = {
+        @Param(name = "self", type = String.class, doc = "This string."),
+        @Param(name = "sub", type = String.class, doc = "The substring to count.")},
+      optionalPositionals = {
+        @Param(name = "start", type = Integer.class, defaultValue = "0",
+            doc = "Restrict to search from this position."),
+        @Param(name = "end", type = Integer.class, noneable = true, defaultValue = "None",
+            doc = "optional position before which to restrict to search.")})
   private static Function count =
       new MixedModeFunction("count", ImmutableList.of("this", "sub", "start", "end"), 2, false) {
         @Override
@@ -354,15 +394,18 @@
         }
       };
 
-  @SkylarkBuiltin(name = "endswith", objectType = StringModule.class, returnType = Boolean.class,
+  @SkylarkSignature(name = "endswith", objectType = StringModule.class, returnType = Boolean.class,
       doc = "Returns True if the string ends with <code>sub</code>, "
           + "otherwise False, optionally restricting to [<code>start</code>:<code>end</code>], "
           + "<code>start</code> being inclusive and <code>end</code> being exclusive.",
-      mandatoryParams = {
-      @Param(name = "sub", type = String.class, doc = "The substring to check.")},
-      optionalParams = {
-      @Param(name = "start", type = Integer.class, doc = "Test beginning at this position."),
-      @Param(name = "end", type = Integer.class, doc = "Stop comparing at this position.")})
+      mandatoryPositionals = {
+        @Param(name = "self", type = String.class, doc = "This string."),
+        @Param(name = "sub", type = String.class, doc = "The substring to check.")},
+      optionalPositionals = {
+        @Param(name = "start", type = Integer.class, defaultValue = "0",
+            doc = "Test beginning at this position."),
+        @Param(name = "end", type = Integer.class, noneable = true, defaultValue = "None",
+            doc = "optional position at which to stop comparing.")})
   private static Function endswith =
       new MixedModeFunction("endswith", ImmutableList.of("this", "sub", "start", "end"), 2, false) {
         @Override
@@ -379,15 +422,19 @@
         }
       };
 
-  @SkylarkBuiltin(name = "startswith", objectType = StringModule.class, returnType = Boolean.class,
+  @SkylarkSignature(name = "startswith", objectType = StringModule.class,
+      returnType = Boolean.class,
       doc = "Returns True if the string starts with <code>sub</code>, "
           + "otherwise False, optionally restricting to [<code>start</code>:<code>end</code>], "
           + "<code>start</code> being inclusive and <code>end</code> being exclusive.",
-      mandatoryParams = {
-      @Param(name = "sub", type = String.class, doc = "The substring to check.")},
-      optionalParams = {
-      @Param(name = "start", type = Integer.class, doc = "Test beginning at this position."),
-      @Param(name = "end", type = Integer.class, doc = "Stop comparing at this position.")})
+      mandatoryPositionals = {
+        @Param(name = "self", type = String.class, doc = "This string."),
+        @Param(name = "sub", type = String.class, doc = "The substring to check.")},
+      optionalPositionals = {
+        @Param(name = "start", type = Integer.class, defaultValue = "0",
+            doc = "Test beginning at this position."),
+        @Param(name = "end", type = Integer.class, noneable = true, defaultValue = "None",
+            doc = "Stop comparing at this position.")})
   private static Function startswith =
     new MixedModeFunction("startswith", ImmutableList.of("this", "sub", "start", "end"), 2, false) {
     @Override
@@ -404,9 +451,11 @@
   };
 
   // TODO(bazel-team): Maybe support an argument to tell the type of the whitespace.
-  @SkylarkBuiltin(name = "strip", objectType = StringModule.class, returnType = String.class,
+  @SkylarkSignature(name = "strip", objectType = StringModule.class, returnType = String.class,
       doc = "Returns a copy of the string in which all whitespace characters "
-          + "have been stripped from the beginning and the end of the string.")
+          + "have been stripped from the beginning and the end of the string.",
+      mandatoryPositionals = {
+        @Param(name = "self", type = String.class, doc = "This string.")})
   private static Function strip =
       new MixedModeFunction("strip", ImmutableList.of("this"), 1, false) {
         @Override
@@ -418,8 +467,13 @@
       };
 
   // slice operator
-  @SkylarkBuiltin(name = "$slice", documented = false,
-      doc = "x[<code>start</code>:<code>end</code>] returns a slice or a list slice.")
+  @SkylarkSignature(name = "$slice", documented = false,
+      mandatoryPositionals = {
+        @Param(name = "self", type = Object.class, doc = "This string, list or tuple."),
+        @Param(name = "start", type = Integer.class, doc = "start position of the slice."),
+        @Param(name = "end", type = Integer.class, doc = "end position of the slice.")},
+      doc = "x[<code>start</code>:<code>end</code>] returns a slice or a list slice.",
+      useLocation = true, useEnvironment = true)
   private static Function slice = new MixedModeFunction("$slice",
       ImmutableList.of("this", "start", "end"), 3, false) {
     @Override
@@ -448,24 +502,34 @@
   };
 
   // supported list methods
-  @SkylarkBuiltin(name = "sorted", doc = "Sort a collection.")
+  @SkylarkSignature(name = "sorted", returnType = HackHackEitherList.class,
+      doc = "Sort a collection.",
+      mandatoryPositionals = {
+        @Param(name = "self", type = HackHackEitherList.class, doc = "This list.")},
+        useLocation = true, useEnvironment = true)
   private static Function sorted = new MixedModeFunction("sorted",
-      ImmutableList.of("this"), 1, false) {
+      ImmutableList.of("self"), 1, false) {
     @Override
     public Object call(Object[] args, FuncallExpression ast, Environment env)
         throws EvalException, ConversionException {
-      List<Object> thiz = Type.OBJECT_LIST.convert(args[0], "'sorted' operand");
+      List<Object> self = Type.OBJECT_LIST.convert(args[0], "'sorted' operand");
       try {
-        thiz = Ordering.from(EvalUtils.SKYLARK_COMPARATOR).sortedCopy(thiz);
+        self = Ordering.from(EvalUtils.SKYLARK_COMPARATOR).sortedCopy(self);
       } catch (EvalUtils.ComparisonException e) {
         throw new EvalException(ast.getLocation(), e);
       }
-      return convert(thiz, env, ast.getLocation());
+      return convert(self, env, ast.getLocation());
     }
   };
 
-  @SkylarkBuiltin(name = "append", documented = false,
-      doc = "Adds an item to the end of the list.")
+  // This function has a SkylarkSignature but is only used by the Build language, not Skylark.
+  @SkylarkSignature(name = "append", returnType = Environment.NoneType.class, documented = false,
+      doc = "Adds an item to the end of the list.",
+      mandatoryPositionals = {
+        // we use List rather than SkylarkList because this is actually for use *outside* Skylark
+        @Param(name = "self", type = List.class, doc = "This list."),
+        @Param(name = "item", type = Object.class, doc = "Item to add at the end.")},
+        useLocation = true, useEnvironment = true)
   private static Function append = new MixedModeFunction("append",
       ImmutableList.of("this", "x"), 2, false) {
     @Override
@@ -477,8 +541,14 @@
     }
   };
 
-  @SkylarkBuiltin(name = "extend", documented = false,
-      doc = "Adds all items to the end of the list.")
+  // This function has a SkylarkSignature but is only used by the Build language, not Skylark.
+  @SkylarkSignature(name = "extend", returnType = Environment.NoneType.class, documented = false,
+      doc = "Adds all items to the end of the list.",
+      mandatoryPositionals = {
+        // we use List rather than SkylarkList because this is actually for use *outside* Skylark
+        @Param(name = "self", type = List.class, doc = "This list."),
+        @Param(name = "items", type = List.class, doc = "Items to add at the end.")},
+        useLocation = true, useEnvironment = true)
   private static Function extend = new MixedModeFunction("extend",
           ImmutableList.of("this", "x"), 2, false) {
     @Override
@@ -492,9 +562,13 @@
   };
 
   // dictionary access operator
-  @SkylarkBuiltin(name = "$index", documented = false,
+  @SkylarkSignature(name = "$index", documented = false,
       doc = "Returns the nth element of a list or string, "
-          + "or looks up a value in a dictionary.")
+          + "or looks up a value in a dictionary.",
+      mandatoryPositionals = {
+        @Param(name = "self", type = Object.class, doc = "This object."),
+        @Param(name = "key", type = Object.class, doc = "The index or key to access.")},
+      useLocation = true)
   private static Function indexOperator = new MixedModeFunction("$index",
       ImmutableList.of("this", "index"), 2, false) {
     @Override
@@ -543,10 +617,13 @@
     }
   };
 
-  @SkylarkBuiltin(name = "values", objectType = DictModule.class, returnType = SkylarkList.class,
+  @SkylarkSignature(name = "values", objectType = DictModule.class,
+      returnType = HackHackEitherList.class,
       doc = "Return the list of values. Dictionaries are always sorted by their keys:"
           + "<pre class=\"language-python\">"
-          + "{2: \"a\", 4: \"b\", 1: \"c\"}.values() == [\"c\", \"a\", \"b\"]</pre>\n")
+          + "{2: \"a\", 4: \"b\", 1: \"c\"}.values() == [\"c\", \"a\", \"b\"]</pre>\n",
+      mandatoryPositionals = {@Param(name = "self", type = Map.class, doc = "This dict.")},
+      useLocation = true, useEnvironment = true)
   private static Function values = new NoArgFunction("values") {
     @Override
     public Object call(Object self, FuncallExpression ast, Environment env)
@@ -557,11 +634,15 @@
     }
   };
 
-  @SkylarkBuiltin(name = "items", objectType = DictModule.class, returnType = SkylarkList.class,
+  @SkylarkSignature(name = "items", objectType = DictModule.class,
+      returnType = HackHackEitherList.class,
       doc = "Return the list of key-value tuples. Dictionaries are always sorted by their keys:"
           + "<pre class=\"language-python\">"
           + "{2: \"a\", 4: \"b\", 1: \"c\"}.items() == [(1, \"c\"), (2, \"a\"), (4, \"b\")]"
-          + "</pre>\n")
+          + "</pre>\n",
+      mandatoryPositionals = {
+        @Param(name = "self", type = Map.class, doc = "This dict.")},
+      useLocation = true, useEnvironment = true)
   private static Function items = new NoArgFunction("items") {
     @Override
     public Object call(Object self, FuncallExpression ast, Environment env)
@@ -577,10 +658,14 @@
     }
   };
 
-  @SkylarkBuiltin(name = "keys", objectType = DictModule.class, returnType = SkylarkList.class,
+  @SkylarkSignature(name = "keys", objectType = DictModule.class,
+      returnType = HackHackEitherList.class,
       doc = "Return the list of keys. Dictionaries are always sorted by their keys:"
           + "<pre class=\"language-python\">{2: \"a\", 4: \"b\", 1: \"c\"}.keys() == [1, 2, 4]"
-          + "</pre>\n")
+          + "</pre>\n",
+      mandatoryPositionals = {
+        @Param(name = "self", type = Map.class, doc = "This dict.")},
+      useLocation = true, useEnvironment = true)
   private static Function keys = new NoArgFunction("keys") {
     @Override
     @SuppressWarnings("unchecked") // Skylark will only call this on a dict; and
@@ -592,14 +677,15 @@
     }
   };
 
-  @SkylarkBuiltin(name = "get", objectType = DictModule.class,
+  @SkylarkSignature(name = "get", objectType = DictModule.class,
       doc = "Return the value for <code>key</code> if <code>key</code> is in the dictionary, "
           + "else <code>default</code>. If <code>default</code> is not given, it defaults to "
           + "<code>None</code>, so that this method never throws an error.",
-      mandatoryParams = {
+      mandatoryPositionals = {
+        @Param(name = "self", doc = "This dict."),
         @Param(name = "key", doc = "The key to look for.")},
-      optionalParams = {
-        @Param(name = "default",
+      optionalPositionals = {
+        @Param(name = "default", defaultValue = "None",
             doc = "The default value to use (instead of None) if the key is not found.")})
   private static Function get =
       new MixedModeFunction("get", ImmutableList.of("this", "key", "default"), 2, false) {
@@ -629,7 +715,9 @@
   }
 
   // unary minus
-  @SkylarkBuiltin(name = "-", documented = false, doc = "Unary minus operator.")
+  @SkylarkSignature(name = "-", documented = false, doc = "Unary minus operator.",
+      mandatoryPositionals = {
+        @Param(name = "num", type = Integer.class, doc = "The number to negate.")})
   private static Function minus = new MixedModeFunction("-", ImmutableList.of("this"), 1, false) {
     @Override
     public Object call(Object[] args, FuncallExpression ast) throws ConversionException {
@@ -638,12 +726,13 @@
     }
   };
 
-  @SkylarkBuiltin(name = "list", returnType = SkylarkList.class,
+  @SkylarkSignature(name = "list", returnType = SkylarkList.class,
       doc = "Converts a collection (e.g. set or dictionary) to a list."
         + "<pre class=\"language-python\">list([1, 2]) == [1, 2]\n"
         + "list(set([2, 3, 2])) == [2, 3]\n"
         + "list({5: \"a\", 2: \"b\", 4: \"c\"}) == [2, 4, 5]</pre>",
-      mandatoryParams = {@Param(name = "x", doc = "The object to convert.")})
+      mandatoryPositionals = {@Param(name = "x", doc = "The object to convert.")},
+      useLocation = true)
     private static Function list = new MixedModeFunction("list",
         ImmutableList.of("list"), 1, false) {
     @Override
@@ -653,9 +742,10 @@
     }
   };
 
-  @SkylarkBuiltin(name = "len", returnType = Integer.class, doc =
+  @SkylarkSignature(name = "len", returnType = Integer.class, doc =
       "Returns the length of a string, list, tuple, set, or dictionary.",
-      mandatoryParams = {@Param(name = "x", doc = "The object to check length of.")})
+      mandatoryPositionals = {@Param(name = "x", doc = "The object to check length of.")},
+      useLocation = true)
   private static Function len = new MixedModeFunction("len",
         ImmutableList.of("list"), 1, false) {
 
@@ -671,9 +761,9 @@
     }
   };
 
-  @SkylarkBuiltin(name = "str", returnType = String.class, doc =
+  @SkylarkSignature(name = "str", returnType = String.class, doc =
       "Converts any object to string. This is useful for debugging.",
-      mandatoryParams = {@Param(name = "x", doc = "The object to convert.")})
+      mandatoryPositionals = {@Param(name = "x", doc = "The object to convert.")})
     private static Function str = new MixedModeFunction("str", ImmutableList.of("this"), 1, false) {
     @Override
     public Object call(Object[] args, FuncallExpression ast) throws EvalException {
@@ -681,12 +771,13 @@
     }
   };
 
-  @SkylarkBuiltin(name = "bool", returnType = Boolean.class, doc = "Converts an object to boolean. "
+  @SkylarkSignature(name = "bool", returnType = Boolean.class,
+      doc = "Converts an object to boolean. "
       + "It returns False if the object is None, False, an empty string, the number 0, or an "
       + "empty collection. Otherwise, it returns True. Similarly to Python <code>bool</code> "
       + "is also a type.",
-      mandatoryParams = {@Param(name = "x", doc = "The variable to convert.")})
-      private static Function bool = new MixedModeFunction("bool",
+      mandatoryPositionals = {@Param(name = "x", doc = "The variable to convert.")})
+  private static Function bool = new MixedModeFunction("bool",
           ImmutableList.of("this"), 1, false) {
     @Override
     public Object call(Object[] args, FuncallExpression ast) throws EvalException {
@@ -694,10 +785,12 @@
     }
   };
 
-  @SkylarkBuiltin(name = "int", returnType = Integer.class, doc = "Converts a string to int, "
+  @SkylarkSignature(name = "int", returnType = Integer.class, doc = "Converts a string to int, "
       + "using base 10. It raises an error if the conversion fails."
       + "<pre class=\"language-python\">int(\"123\") == 123</pre>",
-      mandatoryParams = {@Param(name = "x", type = String.class, doc = "The string to convert.")})
+      mandatoryPositionals = {
+        @Param(name = "x", type = String.class, doc = "The string to convert.")},
+      useLocation = true)
   private static Function int_ =
       new MixedModeFunction("int", ImmutableList.of("x"), 1, false) {
         @Override
@@ -713,13 +806,15 @@
         }
       };
 
-  @SkylarkBuiltin(name = "struct", returnType = SkylarkClassObject.class, doc =
+  @SkylarkSignature(name = "struct", returnType = SkylarkClassObject.class, doc =
       "Creates an immutable struct using the keyword arguments as fields. It is used to group "
       + "multiple values together.Example:<br>"
       + "<pre class=\"language-python\">s = struct(x = 2, y = 3)\n"
-      + "return s.x + s.y  # returns 5</pre>")
+      + "return s.x + s.y  # returns 5</pre>",
+      extraKeywords = {
+        @Param(name = "kwarg", doc = "the struct fields")},
+      useLocation = true)
   private static Function struct = new AbstractFunction("struct") {
-
     @Override
     public Object call(List<Object> args, Map<String, Object> kwargs, FuncallExpression ast,
         Environment env) throws EvalException, InterruptedException {
@@ -730,20 +825,21 @@
     }
   };
 
-  @SkylarkBuiltin(name = "set", returnType = SkylarkNestedSet.class,
+  @SkylarkSignature(name = "set", returnType = SkylarkNestedSet.class,
       doc = "Creates a <a href=\"#modules.set\">set</a> from the <code>items</code>."
       + " The set supports nesting other sets of the same element"
       + " type in it. A desired iteration order can also be specified.<br>"
       + " Examples:<br><pre class=\"language-python\">set([\"a\", \"b\"])\n"
       + "set([1, 2, 3], order=\"compile\")</pre>",
-      optionalParams = {
-      @Param(name = "items", type = SkylarkList.class,
-          doc = "The items to initialize the set with. May contain both standalone items and other"
-          + " sets."),
-      @Param(name = "order", type = String.class,
-          doc = "The ordering strategy for the set if it's nested, "
-              + "possible values are: <code>stable</code> (default), <code>compile</code>, "
-              + "<code>link</code> or <code>naive_link</code>.")})
+      optionalPositionals = {
+        @Param(name = "items", type = Object.class, defaultValue = "[]",
+            doc = "The items to initialize the set with. May contain both standalone items "
+            + "and other sets."),
+        @Param(name = "order", type = String.class, defaultValue = "\"stable\"",
+            doc = "The ordering strategy for the set if it's nested, "
+            + "possible values are: <code>stable</code> (default), <code>compile</code>, "
+            + "<code>link</code> or <code>naive_link</code>.")},
+      useLocation = true)
   private static final Function set =
     new MixedModeFunction("set", ImmutableList.of("items", "order"), 0, false) {
     @Override
@@ -757,14 +853,12 @@
     }
   };
 
-  @SkylarkBuiltin(name = "enumerate",  returnType = SkylarkList.class,
+  @SkylarkSignature(name = "enumerate",  returnType = SkylarkList.class,
       doc = "Return a list of pairs (two-element lists), with the index (int) and the item from"
           + " the input list.\n<pre class=\"language-python\">"
           + "enumerate([24, 21, 84]) == [[0, 24], [1, 21], [2, 84]]</pre>\n",
-      mandatoryParams = {
-      @Param(name = "list", type = SkylarkList.class,
-          doc = "input list"),
-      })
+      mandatoryPositionals = {@Param(name = "list", type = SkylarkList.class, doc = "input list")},
+      useLocation = true)
   private static Function enumerate = new MixedModeFunction("enumerate",
       ImmutableList.of("list"), 1, false) {
     @Override
@@ -783,23 +877,25 @@
     }
   };
 
-  @SkylarkBuiltin(name = "range", returnType = SkylarkList.class,
+  @SkylarkSignature(name = "range", returnType = SkylarkList.class,
       doc = "Creates a list where items go from <code>start</code> to <code>stop</code>, using a "
           + "<code>step</code> increment. If a single argument is provided, items will "
           + "range from 0 to that element."
           + "<pre class=\"language-python\">range(4) == [0, 1, 2, 3]\n"
           + "range(3, 9, 2) == [3, 5, 7]\n"
           + "range(3, 0, -1) == [3, 2, 1]</pre>",
-      mandatoryParams = {
-      @Param(name = "start", type = Integer.class,
-          doc = "Value of the first element"),
+      mandatoryPositionals = {
+        @Param(name = "start", type = Integer.class,
+            doc = "Value of the first element if stop is provided, "
+            + "otherwise value of stop and the actual start is 0"),
       },
-      optionalParams = {
-      @Param(name = "stop", type = Integer.class,
-          doc = "The first item <i>not</i> to be included in the resulting list; "
-          + "generation of the list stops before <code>stop</code> is reached."),
-      @Param(name = "step", type = Integer.class,
-          doc = "The increment (default is 1). It may be negative.")})
+      optionalPositionals = {
+        @Param(name = "stop", type = Integer.class, noneable = true,
+            doc = "optional index of the first item <i>not</i> to be included in the "
+            + "resulting list; generation of the list stops before <code>stop</code> is reached."),
+        @Param(name = "step", type = Integer.class,
+            doc = "The increment (default is 1). It may be negative.")},
+      useLocation = true)
   private static final Function range =
     new MixedModeFunction("range", ImmutableList.of("start", "stop", "step"), 1, false) {
     @Override
@@ -838,9 +934,10 @@
    * Returns a function-value implementing "select" (i.e. configurable attributes)
    * in the specified package context.
    */
-  @SkylarkBuiltin(name = "select",
+  @SkylarkSignature(name = "select",
       doc = "Creates a SelectorValue from the dict parameter.",
-      mandatoryParams = {@Param(name = "x", type = Map.class, doc = "The parameter to convert.")})
+      mandatoryPositionals = {
+        @Param(name = "x", type = Map.class, doc = "The parameter to convert.")})
   private static final Function select = new MixedModeFunction("select",
       ImmutableList.of("x"), 1, false) {
       @Override
@@ -858,16 +955,16 @@
   /**
    * Returns true if the object has a field of the given name, otherwise false.
    */
-  @SkylarkBuiltin(name = "hasattr", returnType = Boolean.class,
+  @SkylarkSignature(name = "hasattr", returnType = Boolean.class,
       doc = "Returns True if the object <code>x</code> has a field of the given <code>name</code>, "
           + "otherwise False. Example:<br>"
           + "<pre class=\"language-python\">hasattr(ctx.attr, \"myattr\")</pre>",
-      mandatoryParams = {
-      @Param(name = "object", doc = "The object to check."),
-      @Param(name = "name", type = String.class, doc = "The name of the field.")})
+      mandatoryPositionals = {
+        @Param(name = "object", doc = "The object to check."),
+        @Param(name = "name", type = String.class, doc = "The name of the field.")},
+      useLocation = true, useEnvironment = true)
   private static final Function hasattr =
       new MixedModeFunction("hasattr", ImmutableList.of("object", "name"), 2, false) {
-
     @Override
     public Object call(Object[] args, FuncallExpression ast, Environment env)
         throws EvalException, ConversionException {
@@ -891,19 +988,20 @@
     }
   };
 
-  @SkylarkBuiltin(name = "getattr",
+  @SkylarkSignature(name = "getattr",
       doc = "Returns the struct's field of the given name if exists, otherwise <code>default</code>"
           + " if specified, otherwise rasies an error. For example, <code>getattr(x, \"foobar\")"
           + "</code> is equivalent to <code>x.foobar</code>."
           + "Example:<br>"
           + "<pre class=\"language-python\">getattr(ctx.attr, \"myattr\")\n"
           + "getattr(ctx.attr, \"myattr\", \"mydefault\")</pre>",
-     mandatoryParams = {
-     @Param(name = "object", doc = "The struct which's field is accessed."),
-     @Param(name = "name", doc = "The name of the struct field.")},
-     optionalParams = {
-     @Param(name = "default", doc = "The default value to return in case the struct "
-                                  + "doesn't have a field of the given name.")})
+      mandatoryPositionals = {
+        @Param(name = "object", doc = "The struct which's field is accessed."),
+        @Param(name = "name", doc = "The name of the struct field.")},
+      optionalPositionals = {
+        @Param(name = "default", doc = "The default value to return in case the struct "
+            + "doesn't have a field of the given name.")},
+      useLocation = true)
   private static final Function getattr = new MixedModeFunction(
       "getattr", ImmutableList.of("object", "name", "default"), 2, false) {
     @Override
@@ -924,10 +1022,11 @@
     }
   };
 
-  @SkylarkBuiltin(name = "dir", returnType = SkylarkList.class,
+  @SkylarkSignature(name = "dir", returnType = SkylarkList.class,
       doc = "Returns a list strings: the names of the fields and "
           + "methods of the parameter object.",
-      mandatoryParams = {@Param(name = "object", doc = "The object to check.")})
+      mandatoryPositionals = {@Param(name = "object", doc = "The object to check.")},
+      useLocation = true, useEnvironment = true)
   private static final Function dir = new MixedModeFunction(
       "dir", ImmutableList.of("object"), 1, false) {
     @Override
@@ -950,9 +1049,9 @@
     }
   };
 
-  @SkylarkBuiltin(name = "type", returnType = String.class,
+  @SkylarkSignature(name = "type", returnType = String.class,
       doc = "Returns the type name of its argument.",
-      mandatoryParams = {@Param(name = "object", doc = "The object to check type of.")})
+      mandatoryPositionals = {@Param(name = "object", doc = "The object to check type of.")})
   private static final Function type = new MixedModeFunction("type",
       ImmutableList.of("object"), 1, false) {
     @Override
@@ -962,14 +1061,16 @@
     }
   };
 
-  @SkylarkBuiltin(name = "fail",
+  @SkylarkSignature(name = "fail",
       doc = "Raises an error that cannot be intercepted.",
       returnType = Environment.NoneType.class,
-      mandatoryParams = {
+      mandatoryPositionals = {
         @Param(name = "msg", type = String.class, doc = "Error message to display for the user")},
-      optionalParams = {
-        @Param(name = "attr", type = String.class,
-            doc = "The name of the attribute that caused the error")})
+      optionalPositionals = {
+        @Param(name = "attr", type = String.class, noneable = true,
+            defaultValue = "None",
+            doc = "The name of the attribute that caused the error")},
+      useLocation = true)
   private static final Function fail = new MixedModeFunction(
       "fail", ImmutableList.of("msg", "attr"), 1, false) {
     @Override
@@ -984,13 +1085,14 @@
     }
   };
 
-  @SkylarkBuiltin(name = "print", returnType = Environment.NoneType.class,
+  @SkylarkSignature(name = "print", returnType = Environment.NoneType.class,
       doc = "Prints <code>msg</code> to the console.",
-      mandatoryParams = {
-      @Param(name = "*args", doc = "The objects to print.")},
-      optionalParams = {
-      @Param(name = "sep", type = String.class,
-          doc = "The separator string between the objects, default is space (\" \").")})
+      optionalNamedOnly = {
+        @Param(name = "sep", type = String.class,
+            doc = "The separator string between the objects, default is space (\" \").")},
+      // NB: as compared to Python3, we're missing optional named-only arguments 'end' and 'file'
+      extraPositionals = {@Param(name = "args", doc = "The objects to print.")},
+      useLocation = true, useEnvironment = true)
   private static final Function print = new AbstractFunction("print") {
     @Override
     public Object call(List<Object> args, Map<String, Object> kwargs, FuncallExpression ast,
@@ -1019,7 +1121,7 @@
     }
   };
 
-  @SkylarkBuiltin(name = "zip",
+  @SkylarkSignature(name = "zip",
       doc = "Returns a <code>list</code> of <code>tuple</code>s, where the i-th tuple contains "
           + "the i-th element from each of the argument sequences or iterables. The list has the "
           + "size of the shortest input. With a single iterable argument, it returns a list of "
@@ -1029,7 +1131,8 @@
           + "zip([1, 2])  # == [(1,), (2,)]\n"
           + "zip([1, 2], [3, 4])  # == [(1, 3), (2, 4)]\n"
           + "zip([1, 2], [3, 4, 5])  # == [(1, 3), (2, 4)]</pre>",
-      returnType = SkylarkList.class)
+      extraPositionals = {@Param(name = "args", doc = "lists to zip")},
+      returnType = SkylarkList.class, useLocation = true)
   private static final Function zip = new AbstractFunction("zip") {
     @Override
     public Object call(List<Object> args, Map<String, Object> kwargs, FuncallExpression ast,
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 04ff209..cbc308e 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
@@ -18,11 +18,11 @@
 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.SkylarkBuiltin;
-import com.google.devtools.build.lib.syntax.SkylarkBuiltin.Param;
 import com.google.devtools.build.lib.syntax.SkylarkFunction;
 import com.google.devtools.build.lib.syntax.SkylarkList;
 import com.google.devtools.build.lib.syntax.SkylarkModule;
+import com.google.devtools.build.lib.syntax.SkylarkSignature;
+import com.google.devtools.build.lib.syntax.SkylarkSignature.Param;
 
 import java.util.Map;
 
@@ -36,44 +36,51 @@
     + "Extra helper functions:")
 public class SkylarkNativeModule {
 
-  @SkylarkBuiltin(name = "glob", objectType = SkylarkNativeModule.class, 
+  @SkylarkSignature(name = "glob", objectType = SkylarkNativeModule.class,
+      returnType = SkylarkList.class,
       doc = "Glob returns a list of every file in the current package that:<ul>\n"
           + "<li>Matches at least one pattern in <code>include</code>.</li>\n"
           + "<li>Does not match any of the patterns in <code>exclude</code> "
           + "(default <code>[]</code>).</li></ul>\n"
           + "If the <code>exclude_directories</code> argument is enabled (set to <code>1</code>), "
           + "files of type directory will be omitted from the results (default <code>1</code>).",
-      mandatoryParams = {
+      mandatoryPositionals = {
       @Param(name = "includes", type = SkylarkList.class, generic1 = String.class,
-          doc = "The list of glob patterns to include.")},
-      optionalParams = {
+          defaultValue = "[]", doc = "The list of glob patterns to include.")},
+      optionalPositionals = {
       @Param(name = "excludes", type = SkylarkList.class, generic1 = String.class,
-          doc = "The list of glob patterns to exclude."),
-      @Param(name = "exclude_directories", type = Integer.class,
-          doc = "A flag whether to exclude directories or not.")})
+          defaultValue = "[]", doc = "The list of glob patterns to exclude."),
+      // TODO(bazel-team): accept booleans as well as integers? (and eventually migrate?)
+      @Param(name = "exclude_directories", type = Integer.class, defaultValue = "1",
+          doc = "A flag whether to exclude directories or not.")},
+      useAst = true, useEnvironment = true)
   private static final SkylarkFunction glob = new SkylarkFunction("glob") {
-    @Override
-    public Object call(Map<String, Object> kwargs, FuncallExpression ast, Environment env)
-        throws EvalException, ConversionException, InterruptedException {
-      return PackageFactory.callGlob(null, false, ast, env, new Object[] {
-          kwargs.get("includes"),
-          kwargs.get("excludes"),
-          kwargs.get("exclude_directories")
-      });
-    }
-  };
+      @Override
+      public Object call(Map<String, Object> kwargs, FuncallExpression ast, Environment env)
+          throws EvalException, ConversionException, InterruptedException {
+        return PackageFactory.callGlob(null, false, ast, env, new Object[] {
+              kwargs.get("includes"),
+              kwargs.get("excludes"),
+              kwargs.get("exclude_directories")
+            });
+      }
+    };
 
-  @SkylarkBuiltin(name = "package_group", objectType = SkylarkNativeModule.class, 
+  @SkylarkSignature(name = "package_group", objectType = SkylarkNativeModule.class,
+      returnType = Environment.NoneType.class,
       doc = "This function defines a set of packages and assigns a label to the group. "
           + "The label can be referenced in <code>visibility</code> attributes.",
-      mandatoryParams = {
+      mandatoryNamedOnly = {
       @Param(name = "name", type = String.class,
           doc = "A unique name for this rule.")},
-      optionalParams = {
+      optionalNamedOnly = {
       @Param(name = "packages", type = SkylarkList.class, generic1 = String.class,
+          defaultValue = "[]",
           doc = "A complete enumeration of packages in this group."),
       @Param(name = "includes", type = SkylarkList.class, generic1 = String.class,
-          doc = "Other package groups that are included in this one.")})
+          defaultValue = "[]",
+          doc = "Other package groups that are included in this one.")},
+      useAst = true, useEnvironment = true)
   private static final SkylarkFunction packageGroup = new SkylarkFunction("package_group") {
     @Override
     public Object call(Map<String, Object> kwargs, FuncallExpression ast, Environment env)
@@ -86,19 +93,23 @@
     }
   };
 
-  @SkylarkBuiltin(name = "exports_files", objectType = SkylarkNativeModule.class, 
+  @SkylarkSignature(name = "exports_files", objectType = SkylarkNativeModule.class,
+      returnType = Environment.NoneType.class,
       doc = "Specifies a list of files belonging to this package that are exported to other "
           + "packages but not otherwise mentioned.",
-      mandatoryParams = {
+      mandatoryPositionals = {
       @Param(name = "srcs", type = SkylarkList.class, generic1 = String.class,
           doc = "The list of files to export.")},
-      optionalParams = {
-      @Param(name = "visibility", type = SkylarkList.class, generic1 = String.class,
+      optionalPositionals = {
+      // TODO(bazel-team): make it possible to express the precise type ListOf(LabelDesignator)
+      @Param(name = "visibility", type = SkylarkList.class,
+          noneable = true,
           doc = "A visibility declaration can to be specified. The files will be visible to the "
               + "targets specified. If no visibility is specified, the files will be visible to "
               + "every package."),
-      @Param(name = "licenses", type = SkylarkList.class, generic1 = String.class,
-          doc = "Lincenses to be specified.")})
+      @Param(name = "licenses", type = SkylarkList.class, generic1 = String.class, noneable = true,
+          doc = "Licenses to be specified.")},
+      useAst = true, useEnvironment = true)
   private static final SkylarkFunction exportsFiles = new SkylarkFunction("exports_files") {
     @Override
     public Object call(Map<String, Object> kwargs, FuncallExpression ast, Environment env)
diff --git a/src/main/java/com/google/devtools/build/lib/rules/SkylarkAttr.java b/src/main/java/com/google/devtools/build/lib/rules/SkylarkAttr.java
index 549e025..408c484 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/SkylarkAttr.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/SkylarkAttr.java
@@ -26,13 +26,13 @@
 import com.google.devtools.build.lib.syntax.EvalException;
 import com.google.devtools.build.lib.syntax.FuncallExpression;
 import com.google.devtools.build.lib.syntax.Label;
-import com.google.devtools.build.lib.syntax.SkylarkBuiltin;
-import com.google.devtools.build.lib.syntax.SkylarkBuiltin.Param;
 import com.google.devtools.build.lib.syntax.SkylarkCallbackFunction;
 import com.google.devtools.build.lib.syntax.SkylarkEnvironment;
 import com.google.devtools.build.lib.syntax.SkylarkFunction;
 import com.google.devtools.build.lib.syntax.SkylarkList;
 import com.google.devtools.build.lib.syntax.SkylarkModule;
+import com.google.devtools.build.lib.syntax.SkylarkSignature;
+import com.google.devtools.build.lib.syntax.SkylarkSignature.Param;
 import com.google.devtools.build.lib.syntax.UserDefinedFunction;
 import com.google.devtools.build.lib.util.FileTypeSet;
 
@@ -153,16 +153,20 @@
     }
   }
 
-  @SkylarkBuiltin(name = "int", doc =
+  @SkylarkSignature(name = "int", doc =
       "Creates an attribute of type int.",
       objectType = SkylarkAttr.class,
-      returnType = Attribute.class,
-      optionalParams = {
-      @Param(name = "default", type = Integer.class,
-          doc = DEFAULT_DOC + " If not specified, default is 0."),
-      @Param(name = "flags", type = SkylarkList.class, generic1 = String.class, doc = FLAGS_DOC),
-      @Param(name = "mandatory", type = Boolean.class, doc = MANDATORY_DOC),
-      @Param(name = "cfg", type = ConfigurationTransition.class, doc = CONFIGURATION_DOC)})
+      returnType = Attribute.Builder.class,
+      optionalNamedOnly = {
+        @Param(name = "default", type = Integer.class, defaultValue = "0",
+            doc = DEFAULT_DOC + " If not specified, default is 0."),
+        @Param(name = "flags", type = SkylarkList.class, generic1 = String.class,
+            defaultValue = "[]", doc = FLAGS_DOC),
+        @Param(name = "mandatory", type = Boolean.class, defaultValue = "False",
+            doc = MANDATORY_DOC),
+        @Param(name = "cfg", type = ConfigurationTransition.class, noneable = true,
+            defaultValue = "None", doc = CONFIGURATION_DOC)},
+      useAst = true, useEnvironment = true)
   private static SkylarkFunction integer = new SkylarkFunction("int") {
       @Override
       public Object call(Map<String, Object> kwargs, FuncallExpression ast, Environment env)
@@ -171,16 +175,20 @@
       }
     };
 
-  @SkylarkBuiltin(name = "string", doc =
+  @SkylarkSignature(name = "string", doc =
       "Creates an attribute of type string.",
       objectType = SkylarkAttr.class,
-      returnType = Attribute.class,
-      optionalParams = {
-      @Param(name = "default", type = String.class,
-          doc = DEFAULT_DOC + " If not specified, default is \"\"."),
-      @Param(name = "flags", type = SkylarkList.class, generic1 = String.class, doc = FLAGS_DOC),
-      @Param(name = "mandatory", type = Boolean.class, doc = MANDATORY_DOC),
-      @Param(name = "cfg", type = ConfigurationTransition.class, doc = CONFIGURATION_DOC)})
+      returnType = Attribute.Builder.class,
+      optionalNamedOnly = {
+        @Param(name = "default", type = String.class,
+            defaultValue = "\"\"", doc = DEFAULT_DOC + " If not specified, default is \"\"."),
+        @Param(name = "flags", type = SkylarkList.class, generic1 = String.class,
+            defaultValue = "[]", doc = FLAGS_DOC),
+        @Param(name = "mandatory", type = Boolean.class,
+            defaultValue = "False", doc = MANDATORY_DOC),
+        @Param(name = "cfg", type = ConfigurationTransition.class, noneable = true,
+            defaultValue = "None", doc = CONFIGURATION_DOC)},
+      useAst = true, useEnvironment = true)
   private static SkylarkFunction string = new SkylarkFunction("string") {
       @Override
       public Object call(Map<String, Object> kwargs, FuncallExpression ast, Environment env)
@@ -189,29 +197,35 @@
       }
     };
 
-  @SkylarkBuiltin(name = "label", doc =
+  @SkylarkSignature(name = "label", doc =
       "Creates an attribute of type Label. "
       + "It is the only way to specify a dependency to another target. "
       + "If you need a dependency that the user cannot overwrite, make the attribute "
       + "private (starts with <code>_</code>).",
       objectType = SkylarkAttr.class,
-      returnType = Attribute.class,
-      optionalParams = {
-      @Param(name = "default", type = Label.class, callbackEnabled = true,
-          doc = DEFAULT_DOC + " If not specified, default is None. "
-              + "Use the <code>Label</code> function to specify a default value."),
-      @Param(name = "executable", type = Boolean.class, doc = EXECUTABLE_DOC),
-      @Param(name = "flags", type = SkylarkList.class, generic1 = String.class, doc = FLAGS_DOC),
-      @Param(name = "allow_files", doc = ALLOW_FILES_DOC),
-      @Param(name = "mandatory", type = Boolean.class, doc = MANDATORY_DOC),
-      @Param(name = "providers", type = SkylarkList.class, generic1 = String.class,
-          doc = "mandatory providers every dependency has to have"),
-      @Param(name = "allow_rules", type = SkylarkList.class, generic1 = String.class,
-          doc = ALLOW_RULES_DOC),
-      @Param(name = "single_file", doc =
-            "if True, the label must correspond to a single File. "
-          + "Access it through <code>ctx.file.&lt;attribute_name&gt;</code>."),
-      @Param(name = "cfg", type = ConfigurationTransition.class, doc = CONFIGURATION_DOC)})
+      returnType = Attribute.Builder.class,
+      optionalNamedOnly = {
+        @Param(name = "default", type = Label.class, callbackEnabled = true, noneable = true,
+            defaultValue = "None",
+            doc = DEFAULT_DOC + " If not specified, default is None. "
+            + "Use the <code>Label</code> function to specify a default value."),
+        @Param(name = "executable", type = Boolean.class, defaultValue = "False",
+            doc = EXECUTABLE_DOC),
+        @Param(name = "flags", type = SkylarkList.class, generic1 = String.class,
+            defaultValue = "[]", doc = FLAGS_DOC),
+        @Param(name = "allow_files", defaultValue = "False", doc = ALLOW_FILES_DOC),
+        @Param(name = "mandatory", type = Boolean.class, defaultValue = "False",
+            doc = MANDATORY_DOC),
+        @Param(name = "providers", type = SkylarkList.class, generic1 = String.class,
+            defaultValue = "[]", doc = "mandatory providers every dependency has to have"),
+        @Param(name = "allow_rules", type = SkylarkList.class, generic1 = String.class,
+            noneable = true, defaultValue = "None", doc = ALLOW_RULES_DOC),
+        @Param(name = "single_file", type = Boolean.class, defaultValue = "False",
+            doc = "if True, the label must correspond to a single File. "
+            + "Access it through <code>ctx.file.&lt;attribute_name&gt;</code>."),
+        @Param(name = "cfg", type = ConfigurationTransition.class, noneable = true,
+            defaultValue = "None", doc = CONFIGURATION_DOC)},
+      useAst = true, useEnvironment = true)
   private static SkylarkFunction label = new SkylarkFunction("label") {
       @Override
       public Object call(Map<String, Object> kwargs, FuncallExpression ast, Environment env)
@@ -220,11 +234,11 @@
       }
     };
 
-  @SkylarkBuiltin(name = "string_list", doc =
+  @SkylarkSignature(name = "string_list", doc =
       "Creates an attribute of type list of strings",
       objectType = SkylarkAttr.class,
       returnType = Attribute.class,
-      optionalParams = {
+      optionalPositionals = {
       @Param(name = "default", type = SkylarkList.class, generic1 = String.class,
           doc = DEFAULT_DOC + " If not specified, default is []."),
       @Param(name = "flags", type = SkylarkList.class, generic1 = String.class, doc = FLAGS_DOC),
@@ -240,25 +254,31 @@
       }
     };
 
-  @SkylarkBuiltin(name = "label_list", doc =
+  @SkylarkSignature(name = "label_list", doc =
         "Creates an attribute of type list of labels. "
       + "See <a href=\"#modules.attr.label\">label</a> for more information.",
       objectType = SkylarkAttr.class,
-      returnType = Attribute.class,
-      optionalParams = {
-      @Param(name = "default", type = SkylarkList.class, generic1 = Label.class,
-          callbackEnabled = true,
-          doc = DEFAULT_DOC + " If not specified, default is []. "
-              + "Use the <code>Label</code> function to specify a default value."),
-      @Param(name = "flags", type = SkylarkList.class, generic1 = String.class, doc = FLAGS_DOC),
-      @Param(name = "allow_files", doc = ALLOW_FILES_DOC),
-      @Param(name = "mandatory", type = Boolean.class, doc = MANDATORY_DOC),
-      @Param(name = "allow_rules", type = SkylarkList.class, generic1 = String.class,
-          doc = ALLOW_RULES_DOC),
-      @Param(name = "non_empty", type = Boolean.class, doc = NON_EMPTY_DOC),
-      @Param(name = "providers", type = SkylarkList.class, generic1 = String.class,
-          doc = "mandatory providers every dependency has to have"),
-      @Param(name = "cfg", type = ConfigurationTransition.class, doc = CONFIGURATION_DOC)})
+      returnType = Attribute.Builder.class,
+      optionalNamedOnly = {
+        @Param(name = "default", type = SkylarkList.class, generic1 = Label.class,
+            callbackEnabled = true, defaultValue = "[]",
+            doc = DEFAULT_DOC + " If not specified, default is []. "
+            + "Use the <code>Label</code> function to specify a default value."),
+        @Param(name = "allow_files", // bool or FileType filter
+            defaultValue = "False", doc = ALLOW_FILES_DOC),
+        @Param(name = "allow_rules", type = SkylarkList.class, generic1 = String.class,
+            noneable = true, defaultValue = "None", doc = ALLOW_RULES_DOC),
+        @Param(name = "providers", type = SkylarkList.class, generic1 = String.class,
+            defaultValue = "[]", doc = "mandatory providers every dependency has to have"),
+        @Param(name = "flags", type = SkylarkList.class, generic1 = String.class,
+            defaultValue = "[]", doc = FLAGS_DOC),
+        @Param(name = "mandatory", type = Boolean.class, defaultValue = "False",
+            doc = MANDATORY_DOC),
+        @Param(name = "non_empty", type = Boolean.class, defaultValue = "False",
+            doc = NON_EMPTY_DOC),
+        @Param(name = "cfg", type = ConfigurationTransition.class, noneable = true,
+            defaultValue = "None", doc = CONFIGURATION_DOC)},
+      useAst = true, useEnvironment = true)
   private static SkylarkFunction labelList = new SkylarkFunction("label_list") {
       @Override
       public Object call(Map<String, Object> kwargs, FuncallExpression ast, Environment env)
@@ -267,15 +287,20 @@
       }
     };
 
-  @SkylarkBuiltin(name = "bool", doc =
+  @SkylarkSignature(name = "bool", doc =
       "Creates an attribute of type bool. Its default value is False.",
       objectType = SkylarkAttr.class,
-      returnType = Attribute.class,
-      optionalParams = {
-      @Param(name = "default", type = Boolean.class, doc = DEFAULT_DOC),
-      @Param(name = "flags", type = SkylarkList.class, generic1 = String.class, doc = FLAGS_DOC),
-      @Param(name = "mandatory", type = Boolean.class, doc = MANDATORY_DOC),
-      @Param(name = "cfg", type = ConfigurationTransition.class, doc = CONFIGURATION_DOC)})
+      returnType = Attribute.Builder.class,
+      optionalNamedOnly = {
+        @Param(name = "default", type = Boolean.class,
+            defaultValue = "False", doc = DEFAULT_DOC),
+        @Param(name = "flags", type = SkylarkList.class, generic1 = String.class,
+            defaultValue = "[]", doc = FLAGS_DOC),
+        @Param(name = "mandatory", type = Boolean.class, defaultValue = "False",
+            doc = MANDATORY_DOC),
+        @Param(name = "cfg", type = ConfigurationTransition.class, noneable = true,
+            defaultValue = "None", doc = CONFIGURATION_DOC)},
+      useAst = true, useEnvironment = true)
   private static SkylarkFunction bool = new SkylarkFunction("bool") {
       @Override
       public Object call(Map<String, Object> kwargs, FuncallExpression ast, Environment env)
@@ -284,17 +309,22 @@
       }
     };
 
-  @SkylarkBuiltin(name = "output", doc =
+  @SkylarkSignature(name = "output", doc =
         "Creates an attribute of type output. Its default value is None. "
       + "The user provides a file name (string) and the rule must create an action that "
       + "generates the file.",
       objectType = SkylarkAttr.class,
-      returnType = Attribute.class,
-      optionalParams = {
-      @Param(name = "default", type = Label.class, doc = DEFAULT_DOC),
-      @Param(name = "flags", type = SkylarkList.class, generic1 = String.class, doc = FLAGS_DOC),
-      @Param(name = "mandatory", type = Boolean.class, doc = MANDATORY_DOC),
-      @Param(name = "cfg", type = ConfigurationTransition.class, doc = CONFIGURATION_DOC)})
+      returnType = Attribute.Builder.class,
+      optionalNamedOnly = {
+        @Param(name = "default", type = Label.class, noneable = true,
+            defaultValue = "None", doc = DEFAULT_DOC),
+        @Param(name = "flags", type = SkylarkList.class, generic1 = String.class,
+            defaultValue = "[]", doc = FLAGS_DOC),
+        @Param(name = "mandatory", type = Boolean.class, defaultValue = "False",
+            doc = MANDATORY_DOC),
+        @Param(name = "cfg", type = ConfigurationTransition.class, noneable = true,
+            defaultValue = "None", doc = CONFIGURATION_DOC)},
+      useAst = true, useEnvironment = true)
   private static SkylarkFunction output = new SkylarkFunction("output") {
       @Override
       public Object call(Map<String, Object> kwargs, FuncallExpression ast, Environment env)
@@ -303,17 +333,23 @@
       }
     };
 
-  @SkylarkBuiltin(name = "output_list", doc =
+  @SkylarkSignature(name = "output_list", doc =
         "Creates an attribute of type list of outputs. Its default value is <code>[]</code>. "
       + "See <a href=\"#modules.attr.output\">output</a> above for more information.",
       objectType = SkylarkAttr.class,
-      returnType = Attribute.class,
-      optionalParams = {
-      @Param(name = "default", type = SkylarkList.class, generic1 = Label.class, doc = DEFAULT_DOC),
-      @Param(name = "flags", type = SkylarkList.class, generic1 = String.class, doc = FLAGS_DOC),
-      @Param(name = "mandatory", type = Boolean.class, doc = MANDATORY_DOC),
-      @Param(name = "non_empty", type = Boolean.class, doc = NON_EMPTY_DOC),
-      @Param(name = "cfg", type = ConfigurationTransition.class, doc = CONFIGURATION_DOC)})
+      returnType = Attribute.Builder.class,
+      optionalNamedOnly = {
+        @Param(name = "default", type = SkylarkList.class, generic1 = Label.class,
+            defaultValue = "[]", doc = DEFAULT_DOC),
+        @Param(name = "flags", type = SkylarkList.class, generic1 = String.class,
+            defaultValue = "[]", doc = FLAGS_DOC),
+        @Param(name = "mandatory", type = Boolean.class,
+            defaultValue = "False", doc = MANDATORY_DOC),
+        @Param(name = "non_empty", type = Boolean.class, defaultValue = "False",
+            doc = NON_EMPTY_DOC),
+        @Param(name = "cfg", type = ConfigurationTransition.class, noneable = true,
+            defaultValue = "None", doc = CONFIGURATION_DOC)},
+      useAst = true, useEnvironment = true)
   private static SkylarkFunction outputList = new SkylarkFunction("output_list") {
       @Override
       public Object call(Map<String, Object> kwargs, FuncallExpression ast, Environment env)
@@ -322,17 +358,23 @@
       }
     };
 
-  @SkylarkBuiltin(name = "string_dict", doc =
+  @SkylarkSignature(name = "string_dict", doc =
       "Creates an attribute of type dictionary, mapping from string to string. "
-      + "Its default value is {}.",
+      + "Its default value is dict().",
       objectType = SkylarkAttr.class,
-      returnType = Attribute.class,
-      optionalParams = {
-      @Param(name = "default", type = Map.class, doc = DEFAULT_DOC),
-      @Param(name = "flags", type = SkylarkList.class, generic1 = String.class, doc = FLAGS_DOC),
-      @Param(name = "mandatory", type = Boolean.class, doc = MANDATORY_DOC),
-      @Param(name = "non_empty", type = Boolean.class, doc = NON_EMPTY_DOC),
-      @Param(name = "cfg", type = ConfigurationTransition.class, doc = CONFIGURATION_DOC)})
+      returnType = Attribute.Builder.class,
+      optionalNamedOnly = {
+        @Param(name = "default", type = Map.class,
+            defaultValue = "{}", doc = DEFAULT_DOC),
+        @Param(name = "flags", type = SkylarkList.class, generic1 = String.class,
+            defaultValue = "[]", doc = FLAGS_DOC),
+        @Param(name = "mandatory", type = Boolean.class, defaultValue = "False",
+            doc = MANDATORY_DOC),
+        @Param(name = "non_empty", type = Boolean.class, defaultValue = "False",
+            doc = NON_EMPTY_DOC),
+        @Param(name = "cfg", type = ConfigurationTransition.class, noneable = true,
+            defaultValue = "None", doc = CONFIGURATION_DOC)},
+      useAst = true, useEnvironment = true)
   private static SkylarkFunction stringDict = new SkylarkFunction("string_dict") {
       @Override
       public Object call(Map<String, Object> kwargs, FuncallExpression ast, Environment env)
@@ -341,16 +383,22 @@
       }
     };
 
-  @SkylarkBuiltin(name = "license", doc =
+  @SkylarkSignature(name = "license", doc =
       "Creates an attribute of type license. Its default value is NO_LICENSE.",
       // TODO(bazel-team): Implement proper license support for Skylark.
       objectType = SkylarkAttr.class,
-      returnType = Attribute.class,
-      optionalParams = {
-      @Param(name = "default", doc = DEFAULT_DOC),
-      @Param(name = "flags", type = SkylarkList.class, generic1 = String.class, doc = FLAGS_DOC),
-      @Param(name = "mandatory", type = Boolean.class, doc = MANDATORY_DOC),
-      @Param(name = "cfg", type = ConfigurationTransition.class, doc = CONFIGURATION_DOC)})
+      returnType = Attribute.Builder.class,
+      optionalNamedOnly = {
+        // TODO(bazel-team): ensure this is the correct default value
+        @Param(name = "default", defaultValue = "None", noneable = true,
+            doc = DEFAULT_DOC),
+        @Param(name = "flags", type = SkylarkList.class, generic1 = String.class,
+            defaultValue = "[]", doc = FLAGS_DOC),
+        @Param(name = "mandatory", type = Boolean.class, defaultValue = "False",
+            doc = MANDATORY_DOC),
+        @Param(name = "cfg", type = ConfigurationTransition.class, noneable = true,
+            defaultValue = "None", doc = CONFIGURATION_DOC)},
+      useAst = true, useEnvironment = true)
   private static SkylarkFunction license = new SkylarkFunction("license") {
       @Override
       public Object call(Map<String, Object> kwargs, FuncallExpression ast, Environment env)
diff --git a/src/main/java/com/google/devtools/build/lib/rules/SkylarkCommandLine.java b/src/main/java/com/google/devtools/build/lib/rules/SkylarkCommandLine.java
index dbf7218..260c7e4 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/SkylarkCommandLine.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/SkylarkCommandLine.java
@@ -20,12 +20,12 @@
 import com.google.devtools.build.lib.collect.nestedset.NestedSet;
 import com.google.devtools.build.lib.events.Location;
 import com.google.devtools.build.lib.syntax.EvalException;
-import com.google.devtools.build.lib.syntax.SkylarkBuiltin;
-import com.google.devtools.build.lib.syntax.SkylarkBuiltin.Param;
 import com.google.devtools.build.lib.syntax.SkylarkFunction.SimpleSkylarkFunction;
 import com.google.devtools.build.lib.syntax.SkylarkList;
 import com.google.devtools.build.lib.syntax.SkylarkModule;
 import com.google.devtools.build.lib.syntax.SkylarkNestedSet;
+import com.google.devtools.build.lib.syntax.SkylarkSignature;
+import com.google.devtools.build.lib.syntax.SkylarkSignature.Param;
 
 import java.util.Map;
 
@@ -36,12 +36,12 @@
     doc = "Module for creating memory efficient command lines.")
 public class SkylarkCommandLine {
 
-  @SkylarkBuiltin(name = "join_paths",
+  @SkylarkSignature(name = "join_paths",
       doc = "Creates a single command line argument joining the paths of a set "
           + "of files on the separator string.",
       objectType = SkylarkCommandLine.class,
       returnType = String.class,
-      mandatoryParams = {
+      mandatoryPositionals = {
       @Param(name = "separator", type = String.class, doc = "the separator string to join on"),
       @Param(name = "files", type = SkylarkNestedSet.class, generic1 = Artifact.class,
              doc = "the files to concatenate")})
@@ -59,11 +59,11 @@
   };
 
   // TODO(bazel-team): this method should support sets of objects and substitute all struct fields.
-  @SkylarkBuiltin(name = "template",
+  @SkylarkSignature(name = "template",
       doc = "Transforms a set of files to a list of strings using the template string.",
       objectType = SkylarkCommandLine.class,
       returnType = SkylarkList.class,
-      mandatoryParams = {
+      mandatoryPositionals = {
       @Param(name = "items", type = SkylarkNestedSet.class, generic1 = Artifact.class,
           doc = "The set of structs to transform."),
       @Param(name = "template", type = String.class,
diff --git a/src/main/java/com/google/devtools/build/lib/rules/SkylarkModules.java b/src/main/java/com/google/devtools/build/lib/rules/SkylarkModules.java
index 7ab0cee..226ceeb 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/SkylarkModules.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/SkylarkModules.java
@@ -24,10 +24,10 @@
 import com.google.devtools.build.lib.syntax.Environment;
 import com.google.devtools.build.lib.syntax.EvaluationContext;
 import com.google.devtools.build.lib.syntax.Function;
-import com.google.devtools.build.lib.syntax.SkylarkBuiltin;
 import com.google.devtools.build.lib.syntax.SkylarkEnvironment;
 import com.google.devtools.build.lib.syntax.SkylarkFunction;
 import com.google.devtools.build.lib.syntax.SkylarkModule;
+import com.google.devtools.build.lib.syntax.SkylarkSignature;
 import com.google.devtools.build.lib.syntax.ValidationEnvironment;
 
 import java.lang.reflect.Field;
@@ -150,11 +150,11 @@
       ImmutableList.Builder<Function> functions, ImmutableMap.Builder<String, Object> objects) {
     try {
       for (Field field : type.getDeclaredFields()) {
-        if (field.isAnnotationPresent(SkylarkBuiltin.class)) {
+        if (field.isAnnotationPresent(SkylarkSignature.class)) {
           // Fields in Skylark modules are sometimes private.
-          // Nevertheless they have to be annotated with SkylarkBuiltin.
+          // Nevertheless they have to be annotated with SkylarkSignature.
           field.setAccessible(true);
-          SkylarkBuiltin annotation = field.getAnnotation(SkylarkBuiltin.class);
+          SkylarkSignature annotation = field.getAnnotation(SkylarkSignature.class);
           Object value = field.get(null);
           if (SkylarkFunction.class.isAssignableFrom(field.getType())) {
             SkylarkFunction function = (SkylarkFunction) value;
@@ -179,8 +179,8 @@
    */
   private static void collectSkylarkTypesFromFields(Class<?> classObject, Set<String> builtIn) {
     for (Field field : classObject.getDeclaredFields()) {
-      if (field.isAnnotationPresent(SkylarkBuiltin.class)) {
-        SkylarkBuiltin annotation = field.getAnnotation(SkylarkBuiltin.class);
+      if (field.isAnnotationPresent(SkylarkSignature.class)) {
+        SkylarkSignature annotation = field.getAnnotation(SkylarkSignature.class);
         if (SkylarkFunction.class.isAssignableFrom(field.getType())) {
           // Ignore non-global values.
           if (annotation.objectType().equals(Object.class)) {
diff --git a/src/main/java/com/google/devtools/build/lib/rules/SkylarkRuleClassFunctions.java b/src/main/java/com/google/devtools/build/lib/rules/SkylarkRuleClassFunctions.java
index 68e9cfe..3cb4e28 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/SkylarkRuleClassFunctions.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/SkylarkRuleClassFunctions.java
@@ -55,6 +55,7 @@
 import com.google.devtools.build.lib.packages.Type;
 import com.google.devtools.build.lib.packages.Type.ConversionException;
 import com.google.devtools.build.lib.syntax.AbstractFunction;
+import com.google.devtools.build.lib.syntax.BaseFunction;
 import com.google.devtools.build.lib.syntax.ClassObject;
 import com.google.devtools.build.lib.syntax.ClassObject.SkylarkClassObject;
 import com.google.devtools.build.lib.syntax.Environment;
@@ -64,13 +65,13 @@
 import com.google.devtools.build.lib.syntax.FuncallExpression;
 import com.google.devtools.build.lib.syntax.Function;
 import com.google.devtools.build.lib.syntax.Label;
-import com.google.devtools.build.lib.syntax.SkylarkBuiltin;
-import com.google.devtools.build.lib.syntax.SkylarkBuiltin.Param;
 import com.google.devtools.build.lib.syntax.SkylarkCallbackFunction;
 import com.google.devtools.build.lib.syntax.SkylarkEnvironment;
 import com.google.devtools.build.lib.syntax.SkylarkFunction;
 import com.google.devtools.build.lib.syntax.SkylarkFunction.SimpleSkylarkFunction;
 import com.google.devtools.build.lib.syntax.SkylarkList;
+import com.google.devtools.build.lib.syntax.SkylarkSignature;
+import com.google.devtools.build.lib.syntax.SkylarkSignature.Param;
 import com.google.devtools.build.lib.syntax.UserDefinedFunction;
 
 import java.util.List;
@@ -84,11 +85,11 @@
 public class SkylarkRuleClassFunctions {
 
   //TODO(bazel-team): proper enum support
-  @SkylarkBuiltin(name = "DATA_CFG", returnType = ConfigurationTransition.class,
+  @SkylarkSignature(name = "DATA_CFG", returnType = ConfigurationTransition.class,
       doc = "Experimental. Specifies a transition to the data configuration.")
   private static final Object dataTransition = ConfigurationTransition.DATA;
 
-  @SkylarkBuiltin(name = "HOST_CFG", returnType = ConfigurationTransition.class,
+  @SkylarkSignature(name = "HOST_CFG", returnType = ConfigurationTransition.class,
       doc = "Specifies a transition to the host configuration.")
   private static final Object hostTransition = ConfigurationTransition.HOST;
 
@@ -177,39 +178,42 @@
 
   // TODO(bazel-team): implement attribute copy and other rule properties
 
-  @SkylarkBuiltin(name = "rule", doc =
+  @SkylarkSignature(name = "rule", doc =
       "Creates a new rule. Store it in a global value, so that it can be loaded and called "
       + "from BUILD files.",
       onlyLoadingPhase = true,
-      returnType = Function.class,
-      mandatoryParams = {
-      @Param(name = "implementation", type = UserDefinedFunction.class,
-          doc = "the function implementing this rule, has to have exactly one parameter: "
-             + "<code>ctx</code>. The function is called during 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.")
+      returnType = BaseFunction.class,
+      mandatoryPositionals = {
+        @Param(name = "implementation", type = Function.class,
+            doc = "the function implementing this rule, has to have exactly one parameter: "
+            + "<code>ctx</code>. The function is called during 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.")
       },
-      optionalParams = {
-      @Param(name = "test", type = Boolean.class, doc = "Whether this rule is a test rule. "
-             + "If True, the rule must end with <code>_test</code> (otherwise it cannot)."),
-      @Param(name = "attrs", type = Map.class, doc =
-          "dictionary to declare all the attributes of the rule. It maps from an attribute name "
-          + "to an attribute object (see <a href=\"#modules.attr\">attr</a> module). "
-          + "Attributes starting with <code>_</code> are private, and can be used to add "
-          + "an implicit dependency on a label."),
-      @Param(name = "outputs", doc = "outputs of this rule. "
-          + "It is a dictionary mapping from string to a template name. "
-          + "For example: <code>{\"ext\": \"${name}.ext\"}</code>. <br>"
-          + "The dictionary key becomes a field in <code>ctx.outputs</code>. "
-          // TODO(bazel-team): Make doc more clear, wrt late-bound attributes.
-          + "It may also be a function (which receives <code>ctx.attr</code> as argument) "
-          + "returning such a dictionary."),
-      @Param(name = "executable", type = Boolean.class,
-          doc = "whether this rule always outputs an executable of the same name or not. If True, "
-          + "there must be an action that generates <code>ctx.outputs.executable</code>."),
-      @Param(name = "output_to_genfiles", type = Boolean.class,
-          doc = "If true, the files will be generated in the genfiles directory instead of the "
-          + "bin directory. This is used for compatibility with existing rules.")})
+      optionalPositionals = {
+        @Param(name = "test", type = Boolean.class, defaultValue = "False",
+            doc = "Whether this rule is a test rule. "
+            + "If True, the rule must end with <code>_test</code> (otherwise it cannot)."),
+        @Param(name = "attrs", type = Map.class, 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=\"#modules.attr\">attr</a> module). "
+            + "Attributes starting with <code>_</code> are private, and can be used to add "
+            + "an implicit dependency on a label."),
+        @Param(name = "outputs", type = Map.class, callbackEnabled = true, noneable = true,
+            defaultValue = "None", doc = "outputs of this rule. "
+            + "It is a dictionary mapping from string to a template name. "
+            + "For example: <code>{\"ext\": \"${name}.ext\"}</code>. <br>"
+            + "The dictionary key becomes a field in <code>ctx.outputs</code>. "
+            // TODO(bazel-team): Make doc more clear, wrt late-bound attributes.
+            + "It may also be a function (which receives <code>ctx.attr</code> as argument) "
+            + "returning such a dictionary."),
+        @Param(name = "executable", type = Boolean.class, defaultValue = "False",
+            doc = "whether this rule always outputs an executable of the same name or not. If "
+            + "True, there must be an action that generates <code>ctx.outputs.executable</code>."),
+        @Param(name = "output_to_genfiles", type = Boolean.class, defaultValue = "False",
+            doc = "If true, the files will be generated in the genfiles directory instead of the "
+            + "bin directory. This is used for compatibility with existing rules.")},
+      useAst = true, useEnvironment = true)
   private static final SkylarkFunction rule = new SkylarkFunction("rule") {
 
         @Override
@@ -313,12 +317,13 @@
     }
   }
 
-  @SkylarkBuiltin(name = "Label", doc = "Creates a Label referring to a BUILD target. Use "
+  @SkylarkSignature(name = "Label", doc = "Creates a Label referring to a BUILD target. Use "
       + "this function only when you want to give a default value for the label attributes. "
       + "Example: <br><pre class=language-python>Label(\"//tools:default\")</pre>",
       returnType = Label.class,
-      mandatoryParams = {@Param(name = "label_string", type = String.class,
-            doc = "the label string")})
+      mandatoryPositionals = {@Param(name = "label_string", type = String.class,
+            doc = "the label string")},
+      useLocation = true)
   private static final SkylarkFunction label = new SimpleSkylarkFunction("Label") {
         @Override
         public Object call(Map<String, Object> arguments, Location loc) throws EvalException,
@@ -332,11 +337,11 @@
         }
       };
 
-  @SkylarkBuiltin(name = "FileType",
+  @SkylarkSignature(name = "FileType",
       doc = "Creates a file filter from a list of strings. For example, to match files ending "
       + "with .cc or .cpp, use: <pre class=language-python>FileType([\".cc\", \".cpp\"])</pre>",
       returnType = SkylarkFileType.class,
-      mandatoryParams = {
+      mandatoryPositionals = {
       @Param(name = "types", type = SkylarkList.class, generic1 = String.class,
           doc = "a list of the accepted file extensions")})
   private static final SkylarkFunction fileType = new SimpleSkylarkFunction("FileType") {
@@ -347,7 +352,7 @@
         }
       };
 
-  @SkylarkBuiltin(name = "to_proto",
+  @SkylarkSignature(name = "to_proto",
       doc = "Creates a text message from the struct parameter. This method only works if all "
           + "struct elements (recursively) are strings, ints, booleans, other structs or a "
           + "list of these types. Quotes and new lines in strings are escaped. "
@@ -362,7 +367,12 @@
           + "# key {\n#   inner_key: 1\n# }\n# key {\n#   inner_key: 2\n# }\n\n"
           + "struct(key=struct(inner_key=struct(inner_inner_key='text'))).to_proto()\n"
           + "# key {\n#    inner_key {\n#     inner_inner_key: \"text\"\n#   }\n# }\n</pre>",
-      objectType = SkylarkClassObject.class, returnType = String.class)
+      objectType = SkylarkClassObject.class, returnType = String.class,
+      mandatoryPositionals = {
+        // TODO(bazel-team): shouldn't we accept any ClassObject?
+        @Param(name = "self", type = SkylarkClassObject.class,
+            doc = "this struct")},
+      useLocation = true)
   private static final SkylarkFunction toProto = new SimpleSkylarkFunction("to_proto") {
     @Override
     public Object call(Map<String, Object> arguments, Location loc) throws EvalException,
diff --git a/src/main/java/com/google/devtools/build/lib/rules/SkylarkRuleImplementationFunctions.java b/src/main/java/com/google/devtools/build/lib/rules/SkylarkRuleImplementationFunctions.java
index cc582ab..0019fe8 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/SkylarkRuleImplementationFunctions.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/SkylarkRuleImplementationFunctions.java
@@ -36,12 +36,12 @@
 import com.google.devtools.build.lib.syntax.EvalException;
 import com.google.devtools.build.lib.syntax.EvalUtils;
 import com.google.devtools.build.lib.syntax.Label;
-import com.google.devtools.build.lib.syntax.SkylarkBuiltin;
-import com.google.devtools.build.lib.syntax.SkylarkBuiltin.Param;
 import com.google.devtools.build.lib.syntax.SkylarkFunction;
 import com.google.devtools.build.lib.syntax.SkylarkFunction.SimpleSkylarkFunction;
 import com.google.devtools.build.lib.syntax.SkylarkList;
 import com.google.devtools.build.lib.syntax.SkylarkNestedSet;
+import com.google.devtools.build.lib.syntax.SkylarkSignature;
+import com.google.devtools.build.lib.syntax.SkylarkSignature.Param;
 import com.google.devtools.build.lib.vfs.PathFragment;
 
 import java.util.Map;
@@ -69,32 +69,43 @@
    *         command = 'command',
    *     )
    */
-  @SkylarkBuiltin(name = "action",
+  @SkylarkSignature(name = "action",
       doc = "Creates an action that runs an executable or a shell command.",
       objectType = SkylarkRuleContext.class,
       returnType = Environment.NoneType.class,
-      mandatoryParams = {
-      @Param(name = "outputs", type = SkylarkList.class, generic1 = Artifact.class,
-          doc = "list of the output files of the action")},
-      optionalParams = {
-      @Param(name = "inputs", type = SkylarkList.class, generic1 = Artifact.class,
-          doc = "list of the input files of the action"),
-      @Param(name = "executable", doc = "the executable file to be called by the action"),
-      @Param(name = "arguments", type = SkylarkList.class, generic1 = String.class,
-          doc = "command line arguments of the action"),
-      @Param(name = "mnemonic", type = String.class, doc = "mnemonic"),
-      @Param(name = "command", doc = "shell command to execute"),
-      @Param(name = "progress_message", type = String.class,
-          doc = "progress message to show to the user during the build, e.g. \"Compiling foo.cc to"
-          + " create foo.o\""),
-      @Param(name = "use_default_shell_env", type = Boolean.class,
-          doc = "whether the action should use the built in shell environment or not"),
-      @Param(name = "env", type = Map.class, doc = "sets the dictionary of environment variables"),
-      @Param(name = "execution_requirements", type = Map.class,
-          doc = "information for scheduling the action"),
-      @Param(name = "input_manifests", type = Map.class,
-          doc = "sets the map of input manifests files; "
-              + "they are typicially generated by the command_helper")})
+      mandatoryPositionals = {
+        @Param(name = "self", type = SkylarkRuleContext.class, doc = "This RuleContext.")},
+      mandatoryNamedOnly = {
+        @Param(name = "outputs", type = SkylarkList.class, generic1 = Artifact.class,
+            doc = "list of the output files of the action")},
+      optionalNamedOnly = {
+        @Param(name = "inputs", type = SkylarkList.class, generic1 = Artifact.class,
+            defaultValue = "[]", doc = "list of the input files of the action"),
+        @Param(name = "executable", type = Object.class, // File or PathFragment or None
+            defaultValue = "None",
+            doc = "the executable file to be called by the action"),
+        @Param(name = "arguments", type = SkylarkList.class, generic1 = String.class,
+            defaultValue = "[]", doc = "command line arguments of the action"),
+        @Param(name = "mnemonic", type = String.class, noneable = true,
+            defaultValue = "None", doc = "mnemonic"),
+        @Param(name = "command", type = Object.class, // string or ListOf(string) or NoneType
+            defaultValue = "None", doc = "shell command to execute"),
+        @Param(name = "progress_message", type = String.class, noneable = true,
+            defaultValue = "None",
+            doc = "progress message to show to the user during the build, "
+            + "e.g. \"Compiling foo.cc to create foo.o\""),
+        @Param(name = "use_default_shell_env", type = Boolean.class, defaultValue = "False",
+            doc = "whether the action should use the built in shell environment or not"),
+        @Param(name = "env", type = Map.class, noneable = true, defaultValue = "None",
+            doc = "sets the dictionary of environment variables"),
+        @Param(name = "execution_requirements", type = Map.class, noneable = true,
+            defaultValue = "None",
+            doc = "information for scheduling the action"),
+        @Param(name = "input_manifests", type = Map.class, noneable = true,
+            defaultValue = "None",
+            doc = "sets the map of input manifests files; "
+            + "they are typicially generated by the command_helper")},
+      useLocation = true)
   private static final SkylarkFunction createSpawnAction =
       new SimpleSkylarkFunction("action") {
 
@@ -175,17 +186,17 @@
   };
 
   // TODO(bazel-team): improve this method to be more memory friendly
-  @SkylarkBuiltin(name = "file_action",
+  @SkylarkSignature(name = "file_action",
       doc = "Creates a file write action.",
       objectType = SkylarkRuleContext.class,
-      returnType = Environment.NoneType.class,
-      optionalParams = {
-        @Param(name = "executable", type = Boolean.class,
-            doc = "whether the output file should be executable (default is False)"),
-      },
-      mandatoryParams = {
+      returnType = FileWriteAction.class,
+      mandatoryPositionals = {
+        @Param(name = "self", type = SkylarkRuleContext.class, doc = "this context"),
         @Param(name = "output", type = Artifact.class, doc = "the output file"),
-        @Param(name = "content", type = String.class, doc = "the contents of the file")})
+        @Param(name = "content", type = String.class, doc = "the contents of the file")},
+      optionalPositionals = {
+        @Param(name = "executable", type = Boolean.class,
+            doc = "whether the output file should be executable (default is False)")})
   private static final SkylarkFunction createFileWriteAction =
     new SimpleSkylarkFunction("file_action") {
 
@@ -204,18 +215,22 @@
     }
   };
 
-  @SkylarkBuiltin(name = "template_action",
+  @SkylarkSignature(name = "template_action",
       doc = "Creates a template expansion action.",
       objectType = SkylarkRuleContext.class,
-      returnType = Environment.NoneType.class,
-      mandatoryParams = {
-      @Param(name = "template", type = Artifact.class, doc = "the template file"),
-      @Param(name = "output", type = Artifact.class, doc = "the output file"),
-      @Param(name = "substitutions", type = Map.class,
-             doc = "substitutions to make when expanding the template")},
-      optionalParams = {
-      @Param(name = "executable", type = Boolean.class,
-          doc = "whether the output file should be executable (default is False)")})
+      returnType = TemplateExpansionAction.class,
+      mandatoryPositionals = {
+        @Param(name = "self", type = SkylarkRuleContext.class, doc = "this context")},
+      mandatoryNamedOnly = {
+        @Param(name = "template", type = Artifact.class,
+            doc = "the template file"),
+        @Param(name = "output", type = Artifact.class,
+            doc = "the output file"),
+        @Param(name = "substitutions", type = Map.class,
+            doc = "substitutions to make when expanding the template")},
+      optionalNamedOnly = {
+        @Param(name = "executable", type = Boolean.class,
+            doc = "whether the output file should be executable (default is False)")})
   private static final SkylarkFunction createTemplateAction =
     new SimpleSkylarkFunction("template_action") {
 
@@ -245,12 +260,13 @@
    * A built in Skylark helper function to access the
    * Transitive info providers of Transitive info collections.
    */
-  @SkylarkBuiltin(name = "provider",
+  @SkylarkSignature(name = "provider",
       doc = "Returns the transitive info provider provided by the target.",
-      mandatoryParams = {
-      @Param(name = "target", type = TransitiveInfoCollection.class,
-          doc = "the configured target which provides the provider"),
-      @Param(name = "type", type = String.class, doc = "the class type of the provider")})
+      mandatoryPositionals = {
+        @Param(name = "target", type = TransitiveInfoCollection.class,
+            doc = "the configured target which provides the provider"),
+        @Param(name = "type", type = String.class, doc = "the class type of the provider")},
+      useLocation = true)
   private static final SkylarkFunction provider = new SimpleSkylarkFunction("provider") {
     @Override
     public Object call(Map<String, Object> params, Location loc) throws EvalException {
@@ -271,21 +287,28 @@
   };
 
   // TODO(bazel-team): Remove runfile states from Skylark.
-  @SkylarkBuiltin(name = "runfiles",
+  @SkylarkSignature(name = "runfiles",
       doc = "Creates a runfiles object.",
       objectType = SkylarkRuleContext.class,
       returnType = Runfiles.class,
-          optionalParams = {
-      @Param(name = "files", type = SkylarkList.class, generic1 = Artifact.class,
-          doc = "The list of files to be added to the runfiles."),
-      // TODO(bazel-team): If we have a memory efficient support for lazy list containing NestedSets
-      // we can remove this and just use files = [file] + list(set)
-      @Param(name = "transitive_files", type = SkylarkNestedSet.class, generic1 = Artifact.class,
-          doc = "The (transitive) set of files to be added to the runfiles."),
-      @Param(name = "collect_data", type = Boolean.class, doc = "Whether to collect the data "
-          + "runfiles from the dependencies in srcs, data and deps attributes."),
-      @Param(name = "collect_default", type = Boolean.class, doc = "Whether to collect the default "
-          + "runfiles from the dependencies in srcs, data and deps attributes.")})
+      mandatoryPositionals = {
+        @Param(name = "self", type = SkylarkRuleContext.class, doc = "This context.")},
+      optionalPositionals = {
+        @Param(name = "files", type = SkylarkList.class, generic1 = Artifact.class,
+            defaultValue = "[]", doc = "The list of files to be added to the runfiles."),
+        // TODO(bazel-team): If we have a memory efficient support for lazy list containing
+        // NestedSets we can remove this and just use files = [file] + list(set)
+        // Also, allow empty set for init
+        @Param(name = "transitive_files", type = SkylarkNestedSet.class, generic1 = Artifact.class,
+            noneable = true, defaultValue = "None",
+            doc = "The (transitive) set of files to be added to the runfiles."),
+        @Param(name = "collect_data", type = Boolean.class, defaultValue = "False",
+            doc = "Whether to collect the data "
+            + "runfiles from the dependencies in srcs, data and deps attributes."),
+        @Param(name = "collect_default", type = Boolean.class, defaultValue = "False",
+            doc = "Whether to collect the default "
+            + "runfiles from the dependencies in srcs, data and deps attributes.")},
+      useLocation = true)
   private static final SkylarkFunction runfiles = new SimpleSkylarkFunction("runfiles") {
     @Override
     public Object call(Map<String, Object> params, Location loc) throws EvalException,
@@ -309,15 +332,16 @@
     }
   };
 
-  @SkylarkBuiltin(name = "command_helper", doc = "Experimental. Creates a command helper class.",
+  @SkylarkSignature(name = "command_helper", doc = "Experimental. Creates a command helper class.",
       objectType = SkylarkRuleContext.class,
       returnType = CommandHelper.class,
-      mandatoryParams = {
-      @Param(name = "tools", type = SkylarkList.class, generic1 = TransitiveInfoCollection.class,
-             doc = "list of tools (list of targets)"),
-      @Param(name = "label_dict", type = Map.class,
-             doc = "dictionary of resolved labels and the corresponding list of Files "
-                 + "(a dict of Label : list of Files)")})
+      mandatoryPositionals = {
+        @Param(name = "self", type = SkylarkRuleContext.class, doc = "this RuleContext"),
+        @Param(name = "tools", type = SkylarkList.class, generic1 = TransitiveInfoCollection.class,
+            doc = "list of tools (list of targets)"),
+        @Param(name = "label_dict", type = Map.class, defaultValue = "{}",
+            doc = "dictionary of resolved labels and the corresponding list of Files "
+            + "(a dict of Label : list of Files)")})
   private static final SkylarkFunction createCommandHelper =
       new SimpleSkylarkFunction("command_helper") {
         @SuppressWarnings("unchecked")
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 fb9e2fd..08ea8ee 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
@@ -16,6 +16,7 @@
 import com.google.common.base.Preconditions;
 import com.google.devtools.build.lib.events.Location;
 import com.google.devtools.build.lib.packages.Type.ConversionException;
+import com.google.devtools.build.lib.syntax.SkylarkSignatureProcessor.HackHackEitherList;
 import com.google.devtools.build.lib.syntax.SkylarkType.SkylarkFunctionType;
 
 import java.lang.reflect.InvocationTargetException;
@@ -59,6 +60,9 @@
   // to be used as size of argument array by the outer call method.
   private int innerArgumentCount;
 
+  // The returnType of the method.
+  private Class<?> returnType;
+
 
   /** Create unconfigured function from its name */
   public BuiltinFunction(String name) {
@@ -202,6 +206,7 @@
     enforcedArgumentTypes = new ArrayList<>();
     this.extraArgs = SkylarkSignatureProcessor.getExtraArgs(annotation);
     super.configure(annotation);
+    this.returnType = annotation.returnType();
   }
 
   // finds the method and makes it accessible (which is needed to find it, and later to use it)
@@ -236,8 +241,6 @@
         "bad argument count for %s: method has %s arguments, type list has %s",
         getName(), innerArgumentCount, parameterTypes.length);
 
-    // TODO(bazel-team): also grab the returnType from the annotations,
-    // and check it against method return type
     if (enforcedArgumentTypes != null) {
       for (int i = 0; i < arguments; i++) {
         SkylarkType enforcedType = enforcedArgumentTypes.get(i);
@@ -254,8 +257,7 @@
             // 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) {
-            Preconditions.checkArgument(
-                enforcedType.getType() == parameterType, msg);
+            Preconditions.checkArgument(enforcedType.getType() == parameterType, msg);
           } else {
             Preconditions.checkArgument(
                 parameterType == Object.class || parameterType == null, msg);
@@ -265,6 +267,14 @@
     }
     // No need for the enforcedArgumentTypes List if all the types were Simple
     enforcedArgumentTypes = FunctionSignature.<SkylarkType>valueListOrNull(enforcedArgumentTypes);
+
+    if (returnType != null) {
+      Class<?> type = returnType;
+      if (type == HackHackEitherList.class) {
+        type = Object.class;
+      }
+      Preconditions.checkArgument(type == invokeMethod.getReturnType());
+    }
   }
 
   /** Configure by copying another function's configuration */
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/Environment.java b/src/main/java/com/google/devtools/build/lib/syntax/Environment.java
index bd59512..a4a4bce 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/Environment.java
+++ b/src/main/java/com/google/devtools/build/lib/syntax/Environment.java
@@ -37,14 +37,15 @@
  */
 public class Environment {
 
-  @SkylarkBuiltin(name = "True", returnType = Boolean.class, doc = "Literal for the boolean true.")
+  @SkylarkSignature(name = "True", returnType = Boolean.class,
+      doc = "Literal for the boolean true.")
   private static final Boolean TRUE = true;
 
-  @SkylarkBuiltin(name = "False", returnType = Boolean.class,
+  @SkylarkSignature(name = "False", returnType = Boolean.class,
       doc = "Literal for the boolean false.")
   private static final Boolean FALSE = false;
 
-  @SkylarkBuiltin(name = "PACKAGE_NAME", returnType = String.class,
+  @SkylarkSignature(name = "PACKAGE_NAME", returnType = String.class,
       doc = "The name of the package the rule or build extension is called from. "
           + "This variable is special, because its value comes from outside of the extension "
           + "module (it comes from the BUILD file), so it can only be accessed in functions "
@@ -65,7 +66,7 @@
     private NoneType() {}
   }
 
-  @SkylarkBuiltin(name = "None", returnType = NoneType.class, doc = "Literal for the None value.")
+  @SkylarkSignature(name = "None", returnType = NoneType.class, doc = "Literal for the None value.")
   public static final NoneType NONE = new NoneType();
 
   protected final Map<String, Object> env = new HashMap<>();
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 79f2efa..f32dcdf 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
@@ -14,6 +14,7 @@
 
 package com.google.devtools.build.lib.syntax;
 
+import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Joiner;
 import com.google.common.cache.CacheBuilder;
 import com.google.common.cache.CacheLoader;
@@ -458,7 +459,8 @@
     }
   }
 
-  static boolean isNamespace(Class<?> classObject) {
+  @VisibleForTesting
+  public static boolean isNamespace(Class<?> classObject) {
     return classObject.isAnnotationPresent(SkylarkModule.class)
         && classObject.getAnnotation(SkylarkModule.class).namespace();
   }
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/FunctionSignature.java b/src/main/java/com/google/devtools/build/lib/syntax/FunctionSignature.java
index 49398f5..3792e21 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/FunctionSignature.java
+++ b/src/main/java/com/google/devtools/build/lib/syntax/FunctionSignature.java
@@ -194,7 +194,7 @@
    *
    * <p>V is the class of defaultValues and T is the class of types.
    * When parsing a function definition at compile-time, they are &lt;Expression, Expression&gt;;
-   * when processing a @SkylarkBuiltin annotation at build-time, &lt;Object, SkylarkType&gt;.
+   * when processing a @SkylarkSignature annotation at build-time, &lt;Object, SkylarkType&gt;.
    */
   @AutoValue
   public abstract static class WithValues<V, T> implements Serializable {
@@ -247,7 +247,7 @@
     /**
      * Parse a list of Parameter into a FunctionSignature.
      *
-     * <p>To be used both by the Parser and by the SkylarkBuiltin annotation processor.
+     * <p>To be used both by the Parser and by the SkylarkSignature annotation processor.
      */
     public static <V, T> WithValues<V, T> of(Iterable<Parameter<V, T>> parameters)
         throws SignatureException {
@@ -512,7 +512,7 @@
     return of(0, 0, numMandatory, false, false, names);
   }
 
-  /** Invalid signature from Parser or from SkylarkBuiltin annotations */
+  /** Invalid signature from Parser or from SkylarkSignature annotations */
   protected static class SignatureException extends Exception {
     @Nullable private final Parameter<?, ?> parameter;
 
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/SkylarkBuiltin.java b/src/main/java/com/google/devtools/build/lib/syntax/SkylarkBuiltin.java
deleted file mode 100644
index 894a77a..0000000
--- a/src/main/java/com/google/devtools/build/lib/syntax/SkylarkBuiltin.java
+++ /dev/null
@@ -1,61 +0,0 @@
-// Copyright 2014 Google Inc. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//    http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-package com.google.devtools.build.lib.syntax;
-
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-
-
-/**
- * An annotation to mark built-in keyword argument methods accessible from Skylark.
- */
-@Target({ElementType.FIELD})
-@Retention(RetentionPolicy.RUNTIME)
-public @interface SkylarkBuiltin {
-
-  String name();
-
-  String doc();
-
-  Param[] mandatoryParams() default {};
-
-  Param[] optionalParams() default {};
-
-  boolean documented() default true;
-
-  Class<?> objectType() default Object.class;
-
-  Class<?> returnType() default Object.class;
-
-  boolean onlyLoadingPhase() default false;
-
-  /**
-   * An annotation for parameters of Skylark built-in functions.
-   */
-  @Retention(RetentionPolicy.RUNTIME)
-  public @interface Param {
-
-    String name();
-
-    String doc();
-
-    Class<?> type() default Object.class;
-
-    Class<?> generic1() default Object.class;
-
-    boolean callbackEnabled() default false;
-  }
-}
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/SkylarkFunction.java b/src/main/java/com/google/devtools/build/lib/syntax/SkylarkFunction.java
index b5aa6c9..c90a4ef 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/SkylarkFunction.java
+++ b/src/main/java/com/google/devtools/build/lib/syntax/SkylarkFunction.java
@@ -31,10 +31,10 @@
  * All usable arguments have to be specified. In case of ambiguous arguments (a parameter is
  * specified as positional and keyword arguments in the function call) an exception is thrown.
  */
-public abstract class SkylarkFunction extends AbstractFunction {
+public abstract class SkylarkFunction extends BaseFunction {
 
   private ImmutableList<String> parameters;
-  private ImmutableMap<String, SkylarkBuiltin.Param> parameterTypes;
+  private ImmutableMap<String, SkylarkSignature.Param> parameterTypes;
   private int mandatoryParamNum;
   private boolean configured = false;
   private Class<?> objectType;
@@ -50,19 +50,37 @@
   /**
    * Configures the parameter of this Skylark function using the annotation.
    */
-  public void configure(SkylarkBuiltin annotation) {
+  public void configure(SkylarkSignature annotation) {
     Preconditions.checkState(!configured);
     Preconditions.checkArgument(
         getName().equals(annotation.name()), "%s != %s", getName(), annotation.name());
+    Preconditions.checkArgument(
+        annotation.optionalPositionals().length == 0 || annotation.mandatoryNamedOnly().length == 0,
+        "SkylarkFunction %s: forbidden simultaneous optionalPositionals and mandatoryNamedOnly",
+        getName());
+    Preconditions.checkArgument(
+        annotation.extraPositionals().length == 0 && annotation.extraKeywords().length == 0,
+        "SkylarkFunction %s: forbidden extra arguments", getName());
     mandatoryParamNum = 0;
     ImmutableList.Builder<String> paramListBuilder = ImmutableList.builder();
-    ImmutableMap.Builder<String, SkylarkBuiltin.Param> paramTypeBuilder = ImmutableMap.builder();
-    for (SkylarkBuiltin.Param param : annotation.mandatoryParams()) {
+    ImmutableMap.Builder<String, SkylarkSignature.Param> paramTypeBuilder = ImmutableMap.builder();
+    for (SkylarkSignature.Param param : annotation.mandatoryPositionals()) {
+      if (!param.name().equals("self")) {
+        paramListBuilder.add(param.name());
+        paramTypeBuilder.put(param.name(), param);
+        mandatoryParamNum++;
+      }
+    }
+    for (SkylarkSignature.Param param : annotation.optionalPositionals()) {
+      paramListBuilder.add(param.name());
+      paramTypeBuilder.put(param.name(), param);
+    }
+    for (SkylarkSignature.Param param : annotation.mandatoryNamedOnly()) {
       paramListBuilder.add(param.name());
       paramTypeBuilder.put(param.name(), param);
       mandatoryParamNum++;
     }
-    for (SkylarkBuiltin.Param param : annotation.optionalParams()) {
+    for (SkylarkSignature.Param param : annotation.optionalNamedOnly()) {
       paramListBuilder.add(param.name());
       paramTypeBuilder.put(param.name(), param);
     }
@@ -152,7 +170,7 @@
 
   private void checkTypeAndAddArg(String paramName, Object value,
       ImmutableMap.Builder<String, Object> arguments, Location loc) throws EvalException {
-    SkylarkBuiltin.Param param = parameterTypes.get(paramName);
+    SkylarkSignature.Param param = parameterTypes.get(paramName);
     if (param.callbackEnabled() && value instanceof Function) {
       // If we pass a function as an argument we trust the Function implementation with the type
       // check. It's OK since the function needs to be called manually anyway.
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/SkylarkSignatureProcessor.java b/src/main/java/com/google/devtools/build/lib/syntax/SkylarkSignatureProcessor.java
index bacb78a..1d9cd81 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/SkylarkSignatureProcessor.java
+++ b/src/main/java/com/google/devtools/build/lib/syntax/SkylarkSignatureProcessor.java
@@ -115,6 +115,18 @@
   }
 
   /**
+   * Fake class to use in SkylarkSignature annotations to indicate that either List or SkylarkList
+   * may be used, depending on whether the Build language or Skylark is being evaluated.
+   */
+  // TODO(bazel-team): either make SkylarkList a subclass of List (mutable or immutable throwing
+  // runtime exceptions), or have the Build language use immutable SkylarkList, but either way,
+  // do away with this hack.
+  public static class HackHackEitherList {
+    private HackHackEitherList() { }
+  }
+
+
+  /**
    * Configures the parameter of this Skylark function using the annotation.
    */
   // TODO(bazel-team): Maybe have the annotation be a string representing the
@@ -134,7 +146,7 @@
       return new Parameter.Star<>(null);
     }
     if (param.type() != Object.class) {
-      if (param.type() == List.class) {
+      if (param.type() == HackHackEitherList.class) {
         // NB: a List in the annotation actually indicates either a List or a SkylarkList
         // and we trust the BuiltinFunction to do the enforcement.
         // For automatic document generation purpose, we lie and just say it's a list;
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/SkylarkType.java b/src/main/java/com/google/devtools/build/lib/syntax/SkylarkType.java
index 01fb872..f07fd61 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/SkylarkType.java
+++ b/src/main/java/com/google/devtools/build/lib/syntax/SkylarkType.java
@@ -836,9 +836,9 @@
   }
 
   /**
-   * Creates a SkylarkType from the SkylarkBuiltin annotation.
+   * Creates a SkylarkType from the SkylarkSignature annotation.
    */
-  public static SkylarkType getReturnType(SkylarkBuiltin annotation) {
+  public static SkylarkType getReturnType(SkylarkSignature annotation) {
     if (Function.class.isAssignableFrom(annotation.returnType())) {
       return SkylarkFunctionType.of(annotation.name());
     } else {