Add annotation-processor verification that only one of Param.type or Param.allowedTypes is used.

RELNOTES: None.
PiperOrigin-RevId: 190070309
diff --git a/src/main/java/com/google/devtools/build/lib/skylarkinterface/processor/SkylarkCallableProcessor.java b/src/main/java/com/google/devtools/build/lib/skylarkinterface/processor/SkylarkCallableProcessor.java
index 841bdd1..ef1dc95 100644
--- a/src/main/java/com/google/devtools/build/lib/skylarkinterface/processor/SkylarkCallableProcessor.java
+++ b/src/main/java/com/google/devtools/build/lib/skylarkinterface/processor/SkylarkCallableProcessor.java
@@ -30,6 +30,7 @@
 import javax.lang.model.element.Modifier;
 import javax.lang.model.element.TypeElement;
 import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.MirroredTypeException;
 import javax.tools.Diagnostic;
 
 /**
@@ -48,6 +49,10 @@
  *   The number of method parameters much match the number of annotation-declared parameters
  *   plus the number of interpreter-supplied parameters.
  * </li>
+ * <li>
+ *   Each parameter, if explicitly typed, may only use either 'type' or 'allowedTypes',
+ *   not both.
+ * </li>
  * </ul>
  *
  * <p>These properties can be relied upon at runtime without additional checks.
@@ -55,7 +60,6 @@
 @SupportedAnnotationTypes({"com.google.devtools.build.lib.skylarkinterface.SkylarkCallable"})
 @SupportedSourceVersion(SourceVersion.RELEASE_8)
 public final class SkylarkCallableProcessor extends AbstractProcessor {
-
   private Messager messager;
 
   private static final String LOCATION = "com.google.devtools.build.lib.events.Location";
@@ -104,6 +108,26 @@
                 + "empty)",
                 parameter.name()));
       }
+      if ((parameter.allowedTypes().length > 0)
+          && (!"java.lang.Object".equals(paramTypeFieldCanonicalName(parameter)))) {
+        throw new SkylarkCallableProcessorException(
+            methodElement,
+            String.format("Parameter '%s' has both 'type' and 'allowedTypes' specified. Only"
+                + " one may be specified.",
+            parameter.name()));
+      }
+    }
+  }
+
+  private String paramTypeFieldCanonicalName(Param param) {
+    try {
+      return param.type().toString();
+    } catch (MirroredTypeException exception) {
+      // This is a hack to obtain the actual canonical name of param.type(). Doing this ths
+      // "correct" way results in far less readable code.
+      // Since this processor is only for compile-time checks, this isn't efficiency we need
+      // to worry about.
+      return exception.getTypeMirror().toString();
     }
   }
 
diff --git a/src/test/java/com/google/devtools/build/lib/skylarkinterface/processor/SkylarkCallableProcessorTest.java b/src/test/java/com/google/devtools/build/lib/skylarkinterface/processor/SkylarkCallableProcessorTest.java
index 6459051..7e7291a 100644
--- a/src/test/java/com/google/devtools/build/lib/skylarkinterface/processor/SkylarkCallableProcessorTest.java
+++ b/src/test/java/com/google/devtools/build/lib/skylarkinterface/processor/SkylarkCallableProcessorTest.java
@@ -133,4 +133,15 @@
         .withErrorContaining(
             "Parameter 'a_parameter' has 'None' default value but is not noneable.");
   }
+
+  @Test
+  public void testParamTypeConflict() throws Exception {
+    assertAbout(javaSource())
+        .that(getFile("ParamTypeConflict.java"))
+        .processedWith(new SkylarkCallableProcessor())
+        .failsToCompile()
+        .withErrorContaining(
+            "Parameter 'a_parameter' has both 'type' and 'allowedTypes' specified."
+                + " Only one may be specified.");
+  }
 }
diff --git a/src/test/java/com/google/devtools/build/lib/skylarkinterface/processor/testsources/GoldenCase.java b/src/test/java/com/google/devtools/build/lib/skylarkinterface/processor/testsources/GoldenCase.java
index 815ba22..1eb2077 100644
--- a/src/test/java/com/google/devtools/build/lib/skylarkinterface/processor/testsources/GoldenCase.java
+++ b/src/test/java/com/google/devtools/build/lib/skylarkinterface/processor/testsources/GoldenCase.java
@@ -16,6 +16,7 @@
 
 import com.google.devtools.build.lib.events.Location;
 import com.google.devtools.build.lib.skylarkinterface.Param;
+import com.google.devtools.build.lib.skylarkinterface.ParamType;
 import com.google.devtools.build.lib.skylarkinterface.SkylarkCallable;
 import com.google.devtools.build.lib.syntax.Environment;
 import com.google.devtools.build.lib.syntax.FuncallExpression;
@@ -74,10 +75,14 @@
     parameters = {
       @Param(name = "one", type = String.class, named = true),
       @Param(name = "two", type = Integer.class, named = true),
-      @Param(name = "three", type = String.class, named = true, defaultValue = "None",
-          noneable = true),
+      @Param(name = "three",
+          allowedTypes = {
+              @ParamType(type = String.class),
+              @ParamType(type = Integer.class),
+          },
+          named = true, defaultValue = "None", noneable = true),
     })
-  public String threeArgMethodWithParams(String one, Integer two, String three) {
+  public String threeArgMethodWithParams(String one, Integer two, Object three) {
     return "baz";
   }
 
diff --git a/src/test/java/com/google/devtools/build/lib/skylarkinterface/processor/testsources/ParamTypeConflict.java b/src/test/java/com/google/devtools/build/lib/skylarkinterface/processor/testsources/ParamTypeConflict.java
new file mode 100644
index 0000000..cda282f
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/lib/skylarkinterface/processor/testsources/ParamTypeConflict.java
@@ -0,0 +1,41 @@
+// Copyright 2018 The Bazel Authors. 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.skylarkinterface.processor.testsources;
+
+import com.google.devtools.build.lib.skylarkinterface.Param;
+import com.google.devtools.build.lib.skylarkinterface.ParamType;
+import com.google.devtools.build.lib.skylarkinterface.SkylarkCallable;
+
+/**
+ * Test case for a SkylarkCallable method which has a parameter with both type and allowedTypes
+ * specified.
+ */
+public class ParamTypeConflict {
+
+  @SkylarkCallable(
+    name = "param_type_conflict",
+    doc = "",
+    parameters = {
+      @Param(name = "a_parameter",
+          type = String.class,
+          named = true,
+          allowedTypes = {
+              @ParamType(type = String.class),
+          })
+    })
+  public Integer paramTypeConflict() {
+    return 42;
+  }
+}