Remove special handling of visibility dependencies in DependencyResolver.

This is done by allowing the analysis of a configured target with a null configuration to proceed a little further than before: we start the ConfiguredTargetFunction, but we short-circuit it as soon as we know that it won't work out.

RELNOTES: None.
PiperOrigin-RevId: 234465687
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/DependencyResolver.java b/src/main/java/com/google/devtools/build/lib/analysis/DependencyResolver.java
index f6d2dbf..8d1df1f 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/DependencyResolver.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/DependencyResolver.java
@@ -54,7 +54,6 @@
 import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Map;
-import java.util.Set;
 import javax.annotation.Nullable;
 
 /**
@@ -135,6 +134,10 @@
     }
   }
 
+  /**
+   * What we know about a dependency edge after factoring in the properties of the configured target
+   * that the edge originates from, but not the properties of target it points to.
+   */
   @AutoValue
   abstract static class PartiallyResolvedDependency {
     public abstract Label getLabel();
@@ -276,8 +279,6 @@
     OrderedSetMultimap<DependencyKind, PartiallyResolvedDependency> partiallyResolvedDeps =
         partiallyResolveDependencies(outgoingLabels, fromRule, attributeMap, aspects);
 
-    filterIllegalVisibilityDependencies(node, targetMap, partiallyResolvedDeps);
-
     OrderedSetMultimap<DependencyKind, Dependency> outgoingEdges =
         fullyResolveDependencies(
             partiallyResolvedDeps, targetMap, node.getConfiguration(), trimmingTransitionFactory);
@@ -322,12 +323,9 @@
       }
 
       if (entry.getKey() == VISIBILITY_DEPENDENCY) {
-        // Package groups should be analyzed with the null configuration, but we don't know if this
-        // is actually a package group. So just use no transition until it's known whether that
-        // assumption is correct.
         partiallyResolvedDeps.put(
             VISIBILITY_DEPENDENCY,
-            PartiallyResolvedDependency.of(toLabel, NoTransition.INSTANCE, ImmutableList.of()));
+            PartiallyResolvedDependency.of(toLabel, NullTransition.INSTANCE, ImmutableList.of()));
         continue;
       }
 
@@ -356,49 +354,14 @@
   }
 
   /**
-   * Filter out visibility dependencies that don't point to package groups.
-   *
-   * <p>This should really be done where we filter other illegal kinds of dependencies (e.g. on the
-   * wrong rule class), but we'll either have to figure out a way to report the common mistake of
-   * the visibility attribute of a rule referring to the rule that depends on it (instead of its
-   * package) or decide to live without nice reporting in that case.
-   */
-  private void filterIllegalVisibilityDependencies(
-      TargetAndConfiguration node,
-      Map<Label, Target> targetMap,
-      OrderedSetMultimap<DependencyKind, PartiallyResolvedDependency> partiallyResolvedDeps) {
-    Set<PartiallyResolvedDependency> illegalVisibilityDeps = new LinkedHashSet<>();
-    for (PartiallyResolvedDependency dep : partiallyResolvedDeps.get(VISIBILITY_DEPENDENCY)) {
-      Target toTarget = targetMap.get(dep.getLabel());
-      if (toTarget == null) {
-        // Dependency pointing to non-existent target. This error was reported above, so we can just
-        // ignore this dependency.
-      }
-
-      if (!(toTarget instanceof PackageGroup)) {
-        // Note that this error could also be caught in
-        // AbstractConfiguredTarget.convertVisibility(), but we have an
-        // opportunity here to avoid dependency cycles that result from
-        // the visibility attribute of a rule referring to a rule that
-        // depends on it (instead of its package)
-        invalidPackageGroupReferenceHook(node, dep.getLabel());
-        illegalVisibilityDeps.add(dep);
-      }
-    }
-
-    for (PartiallyResolvedDependency illegalVisibilityDep : illegalVisibilityDeps) {
-      partiallyResolvedDeps.remove(VISIBILITY_DEPENDENCY, illegalVisibilityDep);
-    }
-  }
-
-  /**
    * Factor in the properties of the target where the dependency points to in the dependency edge
    * calculation.
    *
    * <p>The target of the dependency edges depends on two things: the rule that depends on them and
    * the type of target they depend on. This function takes the rule into account. Accordingly, it
    * should <b>NOT</b> get the {@link Rule} instance representing the rule whose dependencies are
-   * being calculated as an argument or its attributes.
+   * being calculated as an argument or its attributes and it should <b>NOT</b> do anything with the
+   * keys of {@code partiallyResolvedDeps} other than passing them on to the output map.
    */
   private OrderedSetMultimap<DependencyKind, Dependency> fullyResolveDependencies(
       OrderedSetMultimap<DependencyKind, PartiallyResolvedDependency> partiallyResolvedDeps,
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/EmptyConfiguredTarget.java b/src/main/java/com/google/devtools/build/lib/analysis/EmptyConfiguredTarget.java
new file mode 100644
index 0000000..90ef920
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/analysis/EmptyConfiguredTarget.java
@@ -0,0 +1,42 @@
+// Copyright 2019 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.analysis;
+
+import com.google.devtools.build.lib.analysis.configuredtargets.AbstractConfiguredTarget;
+import com.google.devtools.build.lib.cmdline.Label;
+import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder;
+import com.google.devtools.build.lib.collect.nestedset.Order;
+import com.google.devtools.build.lib.packages.InfoInterface;
+import com.google.devtools.build.lib.packages.Provider;
+import com.google.devtools.build.lib.skyframe.BuildConfigurationValue;
+import javax.annotation.Nullable;
+
+/** A configured target that is empty. */
+public class EmptyConfiguredTarget extends AbstractConfiguredTarget {
+  public EmptyConfiguredTarget(Label label, BuildConfigurationValue.Key configurationKey) {
+    super(label, configurationKey, NestedSetBuilder.emptySet(Order.STABLE_ORDER));
+  }
+
+  @Nullable
+  @Override
+  protected InfoInterface rawGetSkylarkProvider(Provider.Key providerKey) {
+    return null;
+  }
+
+  @Override
+  protected Object rawGetSkylarkProvider(String providerKey) {
+    return null;
+  }
+}
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/config/TransitionResolver.java b/src/main/java/com/google/devtools/build/lib/analysis/config/TransitionResolver.java
index 4fb189e..c057826a 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/config/TransitionResolver.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/config/TransitionResolver.java
@@ -59,12 +59,18 @@
       Target toTarget,
       @Nullable RuleTransitionFactory trimmingTransitionFactory) {
 
-    // I. Input files and package groups have no configurations. We don't want to duplicate them.
+    // I. The null configuration always remains the null configuration. We could fold this into
+    // (III), but NoTransition doesn't work if the source is the null configuration.
+    if (fromConfig == null) {
+      return NullTransition.INSTANCE;
+    }
+
+    // II. Input files and package groups have no configurations. We don't want to duplicate them.
     if (usesNullConfiguration(toTarget)) {
       return NullTransition.INSTANCE;
     }
 
-    // II. Host configurations never switch to another. All prerequisites of host targets have the
+    // III. Host configurations never switch to another. All prerequisites of host targets have the
     // same host configuration.
     if (fromConfig.isHostConfiguration()) {
       return NoTransition.INSTANCE;
@@ -90,14 +96,14 @@
     // The current transition to apply. When multiple transitions are requested, this is a
     // ComposingTransition, which encapsulates them into a single object so calling code
     // doesn't need special logic for combinations.
-    // III. Apply whatever transition the attribute requires.
+    // IV. Apply whatever transition the attribute requires.
     ConfigurationTransition currentTransition = attributeTransition;
 
-    // IV. Applies any rule transitions associated with the dep target and composes their
+    // V. Applies any rule transitions associated with the dep target and composes their
     // transitions with a passed-in existing transition.
     currentTransition = applyRuleTransition(currentTransition, toTarget);
 
-    // V. Applies a transition to trim the result and returns it. (note: this is a temporary
+    // VI. Applies a transition to trim the result and returns it. (note: this is a temporary
     // feature; see the corresponding methods in ConfiguredRuleClassProvider)
     return applyTransitionFromFactory(currentTransition, toTarget, trimmingTransitionFactory);
   }
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/ConfiguredTargetFunction.java b/src/main/java/com/google/devtools/build/lib/skyframe/ConfiguredTargetFunction.java
index ad2091f..508d56b 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/ConfiguredTargetFunction.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/ConfiguredTargetFunction.java
@@ -33,6 +33,7 @@
 import com.google.devtools.build.lib.analysis.DependencyResolver.AttributeDependencyKind;
 import com.google.devtools.build.lib.analysis.DependencyResolver.DependencyKind;
 import com.google.devtools.build.lib.analysis.DependencyResolver.InconsistentAspectOrderException;
+import com.google.devtools.build.lib.analysis.EmptyConfiguredTarget;
 import com.google.devtools.build.lib.analysis.PlatformSemantics;
 import com.google.devtools.build.lib.analysis.TargetAndConfiguration;
 import com.google.devtools.build.lib.analysis.ToolchainContext;
@@ -235,6 +236,21 @@
       configuration = null;
     }
 
+    if (target.isConfigurable() && configuredTargetKey.getConfigurationKey() == null) {
+      // We somehow ended up in a target that requires a non-null configuration as a dependency of
+      // one that requires a null configuration. This is always an error, but we need to analyze
+      // the dependencies of the latter target to realize that. Short-circuit the evaluation to
+      // avoid doing useless work and running code with a null configuration that's not prepared for
+      // it.
+      return new NonRuleConfiguredTargetValue(
+          new EmptyConfiguredTarget(target.getLabel(), null),
+          GeneratingActions.EMPTY,
+          transitivePackagesForPackageRootResolution == null
+              ? null
+              : transitivePackagesForPackageRootResolution.build(),
+          nonceVersion.get());
+    }
+
     // This line is only needed for accurate error messaging. Say this target has a circular
     // dependency with one of its deps. With this line, loading this target fails so Bazel
     // associates the corresponding error with this target, as expected. Without this line,