Support revisit full binding graph from validation plugins.

RELNOTES=n/a
PiperOrigin-RevId: 504878700
diff --git a/java/dagger/internal/codegen/bindinggraphvalidation/BindingGraphValidationModule.java b/java/dagger/internal/codegen/bindinggraphvalidation/BindingGraphValidationModule.java
index 2df35e8..7e32bec 100644
--- a/java/dagger/internal/codegen/bindinggraphvalidation/BindingGraphValidationModule.java
+++ b/java/dagger/internal/codegen/bindinggraphvalidation/BindingGraphValidationModule.java
@@ -21,6 +21,7 @@
 import dagger.Provides;
 import dagger.internal.codegen.compileroption.CompilerOptions;
 import dagger.internal.codegen.validation.Validation;
+import dagger.internal.codegen.validation.ValidationBindingGraphPlugin;
 import dagger.spi.model.BindingGraphPlugin;
 
 /** Binds the set of {@link BindingGraphPlugin}s used to implement Dagger validation. */
@@ -29,7 +30,7 @@
 
   @Provides
   @Validation
-  static ImmutableSet<BindingGraphPlugin> providePlugins(
+  static ImmutableSet<ValidationBindingGraphPlugin> providePlugins(
       CompositeBindingGraphPlugin.Factory factory,
       CompilerOptions compilerOptions,
       DependencyCycleValidator validation1,
@@ -43,18 +44,19 @@
       ProvisionDependencyOnProducerBindingValidator validation9,
       SetMultibindingValidator validation10,
       SubcomponentFactoryMethodValidator validation11) {
-    ImmutableSet<BindingGraphPlugin> plugins = ImmutableSet.of(
-        validation1,
-        validation2,
-        validation3,
-        validation4,
-        validation5,
-        validation6,
-        validation7,
-        validation8,
-        validation9,
-        validation10,
-        validation11);
+    ImmutableSet<ValidationBindingGraphPlugin> plugins =
+        ImmutableSet.of(
+            validation1,
+            validation2,
+            validation3,
+            validation4,
+            validation5,
+            validation6,
+            validation7,
+            validation8,
+            validation9,
+            validation10,
+            validation11);
     if (compilerOptions.experimentalDaggerErrorMessages()) {
       return ImmutableSet.of(factory.create(plugins));
     } else {
diff --git a/java/dagger/internal/codegen/bindinggraphvalidation/CompositeBindingGraphPlugin.java b/java/dagger/internal/codegen/bindinggraphvalidation/CompositeBindingGraphPlugin.java
index cb1668a..a328158 100644
--- a/java/dagger/internal/codegen/bindinggraphvalidation/CompositeBindingGraphPlugin.java
+++ b/java/dagger/internal/codegen/bindinggraphvalidation/CompositeBindingGraphPlugin.java
@@ -30,6 +30,7 @@
 import dagger.assisted.AssistedFactory;
 import dagger.assisted.AssistedInject;
 import dagger.internal.codegen.validation.DiagnosticMessageGenerator;
+import dagger.internal.codegen.validation.ValidationBindingGraphPlugin;
 import dagger.spi.model.BindingGraph;
 import dagger.spi.model.BindingGraph.ChildFactoryMethodEdge;
 import dagger.spi.model.BindingGraph.ComponentNode;
@@ -38,27 +39,29 @@
 import dagger.spi.model.BindingGraphPlugin;
 import dagger.spi.model.DaggerProcessingEnv;
 import dagger.spi.model.DiagnosticReporter;
+import java.util.HashMap;
 import java.util.Map;
 import java.util.Optional;
 import java.util.Set;
 import javax.tools.Diagnostic;
 
 /**
- * Combines many {@link BindingGraphPlugin} implementations. This helps reduce spam by combining
- * all of the messages that are reported on the root component.
+ * Combines many {@link BindingGraphPlugin} implementations. This helps reduce spam by combining all
+ * of the messages that are reported on the root component.
  */
-final class CompositeBindingGraphPlugin implements BindingGraphPlugin {
+final class CompositeBindingGraphPlugin extends ValidationBindingGraphPlugin {
   @AssistedFactory
   interface Factory {
-    CompositeBindingGraphPlugin create(ImmutableSet<BindingGraphPlugin> plugins);
+    CompositeBindingGraphPlugin create(ImmutableSet<ValidationBindingGraphPlugin> plugins);
   }
 
-  private final ImmutableSet<BindingGraphPlugin> plugins;
+  private final ImmutableSet<ValidationBindingGraphPlugin> plugins;
   private final DiagnosticMessageGenerator.Factory messageGeneratorFactory;
+  private final Map<ComponentNode, String> errorMessages = new HashMap<>();
 
   @AssistedInject
   CompositeBindingGraphPlugin(
-      @Assisted ImmutableSet<BindingGraphPlugin> plugins,
+      @Assisted ImmutableSet<ValidationBindingGraphPlugin> plugins,
       DiagnosticMessageGenerator.Factory messageGeneratorFactory) {
     this.plugins = plugins;
     this.messageGeneratorFactory = messageGeneratorFactory;
@@ -68,10 +71,38 @@
   public void visitGraph(BindingGraph bindingGraph, DiagnosticReporter diagnosticReporter) {
     AggregatingDiagnosticReporter aggregatingDiagnosticReporter = new AggregatingDiagnosticReporter(
         bindingGraph, diagnosticReporter, messageGeneratorFactory.create(bindingGraph));
-    plugins.forEach(plugin -> {
-      aggregatingDiagnosticReporter.setCurrentPlugin(plugin.pluginName());
-      plugin.visitGraph(bindingGraph, aggregatingDiagnosticReporter);
-    });
+    plugins.forEach(
+        plugin -> {
+          aggregatingDiagnosticReporter.setCurrentPlugin(plugin.pluginName());
+          plugin.visitGraph(bindingGraph, aggregatingDiagnosticReporter);
+          if (plugin.visitFullGraphRequested(bindingGraph)) {
+            requestVisitFullGraph(bindingGraph);
+          }
+        });
+    if (visitFullGraphRequested(bindingGraph)) {
+      errorMessages.put(
+          bindingGraph.rootComponentNode(), aggregatingDiagnosticReporter.getMessage());
+    } else {
+      aggregatingDiagnosticReporter.report();
+    }
+  }
+
+  @Override
+  public void revisitFullGraph(
+      BindingGraph prunedGraph, BindingGraph fullGraph, DiagnosticReporter diagnosticReporter) {
+    AggregatingDiagnosticReporter aggregatingDiagnosticReporter =
+        new AggregatingDiagnosticReporter(
+            fullGraph,
+            diagnosticReporter,
+            errorMessages.get(prunedGraph.rootComponentNode()),
+            messageGeneratorFactory.create(fullGraph));
+    plugins.stream()
+        .filter(plugin -> plugin.visitFullGraphRequested(prunedGraph))
+        .forEach(
+            plugin -> {
+              aggregatingDiagnosticReporter.setCurrentPlugin(plugin.pluginName());
+              plugin.revisitFullGraph(prunedGraph, fullGraph, aggregatingDiagnosticReporter);
+            });
     aggregatingDiagnosticReporter.report();
   }
 
@@ -118,6 +149,17 @@
       this.messageGenerator = messageGenerator;
     }
 
+    AggregatingDiagnosticReporter(
+        BindingGraph graph,
+        DiagnosticReporter delegate,
+        String baseMessage,
+        DiagnosticMessageGenerator messageGenerator) {
+      this.graph = graph;
+      this.delegate = delegate;
+      this.messageGenerator = messageGenerator;
+      this.messageBuilder.append(baseMessage);
+    }
+
     /** Sets the currently running aggregated plugin. Used to add a diagnostic prefix. */
     void setCurrentPlugin(String pluginName) {
       currentPluginName = pluginName;
@@ -133,6 +175,10 @@
       }
     }
 
+    String getMessage() {
+      return messageBuilder.toString();
+    }
+
     @Override
     public void reportComponent(Diagnostic.Kind diagnosticKind, ComponentNode componentNode,
         String message) {
diff --git a/java/dagger/internal/codegen/bindinggraphvalidation/DependencyCycleValidator.java b/java/dagger/internal/codegen/bindinggraphvalidation/DependencyCycleValidator.java
index f2cb90b..f42f7ad 100644
--- a/java/dagger/internal/codegen/bindinggraphvalidation/DependencyCycleValidator.java
+++ b/java/dagger/internal/codegen/bindinggraphvalidation/DependencyCycleValidator.java
@@ -44,12 +44,12 @@
 import dagger.internal.codegen.base.OptionalType;
 import dagger.internal.codegen.binding.DependencyRequestFormatter;
 import dagger.internal.codegen.javapoet.TypeNames;
+import dagger.internal.codegen.validation.ValidationBindingGraphPlugin;
 import dagger.spi.model.Binding;
 import dagger.spi.model.BindingGraph;
 import dagger.spi.model.BindingGraph.ComponentNode;
 import dagger.spi.model.BindingGraph.DependencyEdge;
 import dagger.spi.model.BindingGraph.Node;
-import dagger.spi.model.BindingGraphPlugin;
 import dagger.spi.model.BindingKind;
 import dagger.spi.model.DependencyRequest;
 import dagger.spi.model.DiagnosticReporter;
@@ -61,7 +61,7 @@
 import javax.inject.Inject;
 
 /** Reports errors for dependency cycles. */
-final class DependencyCycleValidator implements BindingGraphPlugin {
+final class DependencyCycleValidator extends ValidationBindingGraphPlugin {
 
   private final DependencyRequestFormatter dependencyRequestFormatter;
 
diff --git a/java/dagger/internal/codegen/bindinggraphvalidation/DependsOnProductionExecutorValidator.java b/java/dagger/internal/codegen/bindinggraphvalidation/DependsOnProductionExecutorValidator.java
index 1d44133..4bdff69 100644
--- a/java/dagger/internal/codegen/bindinggraphvalidation/DependsOnProductionExecutorValidator.java
+++ b/java/dagger/internal/codegen/bindinggraphvalidation/DependsOnProductionExecutorValidator.java
@@ -21,10 +21,10 @@
 
 import dagger.internal.codegen.binding.KeyFactory;
 import dagger.internal.codegen.compileroption.CompilerOptions;
+import dagger.internal.codegen.validation.ValidationBindingGraphPlugin;
 import dagger.spi.model.Binding;
 import dagger.spi.model.BindingGraph;
 import dagger.spi.model.BindingGraph.MaybeBinding;
-import dagger.spi.model.BindingGraphPlugin;
 import dagger.spi.model.DiagnosticReporter;
 import dagger.spi.model.Key;
 import javax.inject.Inject;
@@ -33,7 +33,7 @@
  * Reports an error on all bindings that depend explicitly on the {@code @Production Executor} key.
  */
 // TODO(dpb,beder): Validate this during @Inject/@Provides/@Produces validation.
-final class DependsOnProductionExecutorValidator implements BindingGraphPlugin {
+final class DependsOnProductionExecutorValidator extends ValidationBindingGraphPlugin {
   private final CompilerOptions compilerOptions;
   private final KeyFactory keyFactory;
 
diff --git a/java/dagger/internal/codegen/bindinggraphvalidation/DuplicateBindingsValidator.java b/java/dagger/internal/codegen/bindinggraphvalidation/DuplicateBindingsValidator.java
index 286fa7f..2b11f17 100644
--- a/java/dagger/internal/codegen/bindinggraphvalidation/DuplicateBindingsValidator.java
+++ b/java/dagger/internal/codegen/bindinggraphvalidation/DuplicateBindingsValidator.java
@@ -44,10 +44,10 @@
 import dagger.internal.codegen.binding.BindingNode;
 import dagger.internal.codegen.binding.MultibindingDeclaration;
 import dagger.internal.codegen.compileroption.CompilerOptions;
+import dagger.internal.codegen.validation.ValidationBindingGraphPlugin;
 import dagger.spi.model.Binding;
 import dagger.spi.model.BindingGraph;
 import dagger.spi.model.BindingGraph.ComponentNode;
-import dagger.spi.model.BindingGraphPlugin;
 import dagger.spi.model.BindingKind;
 import dagger.spi.model.ComponentPath;
 import dagger.spi.model.DaggerElement;
@@ -63,7 +63,7 @@
 import javax.tools.Diagnostic.Kind;
 
 /** Reports errors for conflicting bindings with the same key. */
-final class DuplicateBindingsValidator implements BindingGraphPlugin {
+final class DuplicateBindingsValidator extends ValidationBindingGraphPlugin {
 
   private static final Comparator<Binding> BY_LENGTH_OF_COMPONENT_PATH =
       comparing(binding -> binding.componentPath().components().size());
diff --git a/java/dagger/internal/codegen/bindinggraphvalidation/IncompatiblyScopedBindingsValidator.java b/java/dagger/internal/codegen/bindinggraphvalidation/IncompatiblyScopedBindingsValidator.java
index b9cf89c..3643870 100644
--- a/java/dagger/internal/codegen/bindinggraphvalidation/IncompatiblyScopedBindingsValidator.java
+++ b/java/dagger/internal/codegen/bindinggraphvalidation/IncompatiblyScopedBindingsValidator.java
@@ -30,10 +30,10 @@
 import dagger.internal.codegen.binding.MethodSignatureFormatter;
 import dagger.internal.codegen.compileroption.CompilerOptions;
 import dagger.internal.codegen.validation.DiagnosticMessageGenerator;
+import dagger.internal.codegen.validation.ValidationBindingGraphPlugin;
 import dagger.spi.model.Binding;
 import dagger.spi.model.BindingGraph;
 import dagger.spi.model.BindingGraph.ComponentNode;
-import dagger.spi.model.BindingGraphPlugin;
 import dagger.spi.model.DiagnosticReporter;
 import java.util.Optional;
 import java.util.Set;
@@ -44,7 +44,7 @@
  * Reports an error for any component that uses bindings with scopes that are not assigned to the
  * component.
  */
-final class IncompatiblyScopedBindingsValidator implements BindingGraphPlugin {
+final class IncompatiblyScopedBindingsValidator extends ValidationBindingGraphPlugin {
 
   private final MethodSignatureFormatter methodSignatureFormatter;
   private final CompilerOptions compilerOptions;
diff --git a/java/dagger/internal/codegen/bindinggraphvalidation/InjectBindingValidator.java b/java/dagger/internal/codegen/bindinggraphvalidation/InjectBindingValidator.java
index ece8bbe..ae153a0 100644
--- a/java/dagger/internal/codegen/bindinggraphvalidation/InjectBindingValidator.java
+++ b/java/dagger/internal/codegen/bindinggraphvalidation/InjectBindingValidator.java
@@ -19,16 +19,16 @@
 import static dagger.spi.model.BindingKind.INJECTION;
 
 import dagger.internal.codegen.validation.InjectValidator;
+import dagger.internal.codegen.validation.ValidationBindingGraphPlugin;
 import dagger.internal.codegen.validation.ValidationReport;
 import dagger.internal.codegen.validation.ValidationReport.Item;
 import dagger.spi.model.Binding;
 import dagger.spi.model.BindingGraph;
-import dagger.spi.model.BindingGraphPlugin;
 import dagger.spi.model.DiagnosticReporter;
 import javax.inject.Inject;
 
 /** Validates bindings from {@code @Inject}-annotated constructors. */
-final class InjectBindingValidator implements BindingGraphPlugin {
+final class InjectBindingValidator extends ValidationBindingGraphPlugin {
   private final InjectValidator injectValidator;
 
   @Inject
diff --git a/java/dagger/internal/codegen/bindinggraphvalidation/MapMultibindingValidator.java b/java/dagger/internal/codegen/bindinggraphvalidation/MapMultibindingValidator.java
index cef4a9b..0698bef 100644
--- a/java/dagger/internal/codegen/bindinggraphvalidation/MapMultibindingValidator.java
+++ b/java/dagger/internal/codegen/bindinggraphvalidation/MapMultibindingValidator.java
@@ -37,9 +37,9 @@
 import dagger.internal.codegen.binding.ContributionBinding;
 import dagger.internal.codegen.binding.KeyFactory;
 import dagger.internal.codegen.javapoet.TypeNames;
+import dagger.internal.codegen.validation.ValidationBindingGraphPlugin;
 import dagger.spi.model.Binding;
 import dagger.spi.model.BindingGraph;
-import dagger.spi.model.BindingGraphPlugin;
 import dagger.spi.model.DiagnosticReporter;
 import dagger.spi.model.Key;
 import java.util.Set;
@@ -49,7 +49,7 @@
  * Reports an error for any map binding with either more than one contribution with the same map key
  * or contributions with inconsistent map key annotation types.
  */
-final class MapMultibindingValidator implements BindingGraphPlugin {
+final class MapMultibindingValidator extends ValidationBindingGraphPlugin {
 
   private final BindingDeclarationFormatter bindingDeclarationFormatter;
   private final KeyFactory keyFactory;
diff --git a/java/dagger/internal/codegen/bindinggraphvalidation/MissingBindingValidator.java b/java/dagger/internal/codegen/bindinggraphvalidation/MissingBindingValidator.java
index fdcccf9..eb0ef6c 100644
--- a/java/dagger/internal/codegen/bindinggraphvalidation/MissingBindingValidator.java
+++ b/java/dagger/internal/codegen/bindinggraphvalidation/MissingBindingValidator.java
@@ -31,6 +31,7 @@
 import dagger.internal.codegen.binding.DependencyRequestFormatter;
 import dagger.internal.codegen.binding.InjectBindingRegistry;
 import dagger.internal.codegen.validation.DiagnosticMessageGenerator;
+import dagger.internal.codegen.validation.ValidationBindingGraphPlugin;
 import dagger.spi.model.Binding;
 import dagger.spi.model.BindingGraph;
 import dagger.spi.model.BindingGraph.ComponentNode;
@@ -38,7 +39,6 @@
 import dagger.spi.model.BindingGraph.Edge;
 import dagger.spi.model.BindingGraph.MissingBinding;
 import dagger.spi.model.BindingGraph.Node;
-import dagger.spi.model.BindingGraphPlugin;
 import dagger.spi.model.ComponentPath;
 import dagger.spi.model.DiagnosticReporter;
 import dagger.spi.model.Key;
@@ -47,7 +47,7 @@
 import javax.inject.Inject;
 
 /** Reports errors for missing bindings. */
-final class MissingBindingValidator implements BindingGraphPlugin {
+final class MissingBindingValidator extends ValidationBindingGraphPlugin {
 
   private final InjectBindingRegistry injectBindingRegistry;
   private final DependencyRequestFormatter dependencyRequestFormatter;
diff --git a/java/dagger/internal/codegen/bindinggraphvalidation/NullableBindingValidator.java b/java/dagger/internal/codegen/bindinggraphvalidation/NullableBindingValidator.java
index 4130fe9..bbc026e 100644
--- a/java/dagger/internal/codegen/bindinggraphvalidation/NullableBindingValidator.java
+++ b/java/dagger/internal/codegen/bindinggraphvalidation/NullableBindingValidator.java
@@ -24,10 +24,10 @@
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
 import dagger.internal.codegen.compileroption.CompilerOptions;
+import dagger.internal.codegen.validation.ValidationBindingGraphPlugin;
 import dagger.spi.model.Binding;
 import dagger.spi.model.BindingGraph;
 import dagger.spi.model.BindingGraph.DependencyEdge;
-import dagger.spi.model.BindingGraphPlugin;
 import dagger.spi.model.DiagnosticReporter;
 import javax.inject.Inject;
 
@@ -35,7 +35,7 @@
  * Reports errors or warnings (depending on the {@code -Adagger.nullableValidation} value) for each
  * non-nullable dependency request that is satisfied by a nullable binding.
  */
-final class NullableBindingValidator implements BindingGraphPlugin {
+final class NullableBindingValidator extends ValidationBindingGraphPlugin {
 
   private final CompilerOptions compilerOptions;
 
diff --git a/java/dagger/internal/codegen/bindinggraphvalidation/ProvisionDependencyOnProducerBindingValidator.java b/java/dagger/internal/codegen/bindinggraphvalidation/ProvisionDependencyOnProducerBindingValidator.java
index 53904a7..7ad0e9f 100644
--- a/java/dagger/internal/codegen/bindinggraphvalidation/ProvisionDependencyOnProducerBindingValidator.java
+++ b/java/dagger/internal/codegen/bindinggraphvalidation/ProvisionDependencyOnProducerBindingValidator.java
@@ -22,11 +22,11 @@
 import static dagger.internal.codegen.extension.DaggerStreams.instancesOf;
 import static javax.tools.Diagnostic.Kind.ERROR;
 
+import dagger.internal.codegen.validation.ValidationBindingGraphPlugin;
 import dagger.spi.model.Binding;
 import dagger.spi.model.BindingGraph;
 import dagger.spi.model.BindingGraph.DependencyEdge;
 import dagger.spi.model.BindingGraph.Node;
-import dagger.spi.model.BindingGraphPlugin;
 import dagger.spi.model.DiagnosticReporter;
 import java.util.stream.Stream;
 import javax.inject.Inject;
@@ -36,7 +36,7 @@
  * binding.
  */
 // TODO(b/29509141): Clarify the error.
-final class ProvisionDependencyOnProducerBindingValidator implements BindingGraphPlugin {
+final class ProvisionDependencyOnProducerBindingValidator extends ValidationBindingGraphPlugin {
 
   @Inject
   ProvisionDependencyOnProducerBindingValidator() {}
diff --git a/java/dagger/internal/codegen/bindinggraphvalidation/SetMultibindingValidator.java b/java/dagger/internal/codegen/bindinggraphvalidation/SetMultibindingValidator.java
index 1f79f87..6b7de40 100644
--- a/java/dagger/internal/codegen/bindinggraphvalidation/SetMultibindingValidator.java
+++ b/java/dagger/internal/codegen/bindinggraphvalidation/SetMultibindingValidator.java
@@ -25,15 +25,15 @@
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Multimap;
+import dagger.internal.codegen.validation.ValidationBindingGraphPlugin;
 import dagger.spi.model.Binding;
 import dagger.spi.model.BindingGraph;
-import dagger.spi.model.BindingGraphPlugin;
 import dagger.spi.model.DiagnosticReporter;
 import dagger.spi.model.Key;
 import javax.inject.Inject;
 
 /** Validates that there are not multiple set binding contributions to the same binding. */
-final class SetMultibindingValidator implements BindingGraphPlugin {
+final class SetMultibindingValidator extends ValidationBindingGraphPlugin {
 
   @Inject
   SetMultibindingValidator() {
diff --git a/java/dagger/internal/codegen/bindinggraphvalidation/SubcomponentFactoryMethodValidator.java b/java/dagger/internal/codegen/bindinggraphvalidation/SubcomponentFactoryMethodValidator.java
index e41c84b..1ecff19 100644
--- a/java/dagger/internal/codegen/bindinggraphvalidation/SubcomponentFactoryMethodValidator.java
+++ b/java/dagger/internal/codegen/bindinggraphvalidation/SubcomponentFactoryMethodValidator.java
@@ -31,10 +31,10 @@
 import com.google.common.collect.Sets.SetView;
 import dagger.internal.codegen.base.Util;
 import dagger.internal.codegen.binding.ComponentNodeImpl;
+import dagger.internal.codegen.validation.ValidationBindingGraphPlugin;
 import dagger.spi.model.BindingGraph;
 import dagger.spi.model.BindingGraph.ChildFactoryMethodEdge;
 import dagger.spi.model.BindingGraph.ComponentNode;
-import dagger.spi.model.BindingGraphPlugin;
 import dagger.spi.model.DiagnosticReporter;
 import java.util.HashMap;
 import java.util.Map;
@@ -43,7 +43,7 @@
 import javax.inject.Inject;
 
 /** Reports an error if a subcomponent factory method is missing required modules. */
-final class SubcomponentFactoryMethodValidator implements BindingGraphPlugin {
+final class SubcomponentFactoryMethodValidator extends ValidationBindingGraphPlugin {
 
   private final Map<ComponentNode, Set<XTypeElement>> inheritedModulesCache = new HashMap<>();
 
diff --git a/java/dagger/internal/codegen/processingstep/BUILD b/java/dagger/internal/codegen/processingstep/BUILD
index 6eda2b4..97d79af 100644
--- a/java/dagger/internal/codegen/processingstep/BUILD
+++ b/java/dagger/internal/codegen/processingstep/BUILD
@@ -34,6 +34,7 @@
         "//java/dagger/internal/codegen/validation",
         "//java/dagger/internal/codegen/writing",
         "//java/dagger/internal/codegen/xprocessing",
+        "//java/dagger/spi",
         "//third_party/java/auto:common",
         "//third_party/java/error_prone:annotations",
         "//third_party/java/guava/base",
diff --git a/java/dagger/internal/codegen/processingstep/ComponentProcessingStep.java b/java/dagger/internal/codegen/processingstep/ComponentProcessingStep.java
index fe83d8c..bac8d21 100644
--- a/java/dagger/internal/codegen/processingstep/ComponentProcessingStep.java
+++ b/java/dagger/internal/codegen/processingstep/ComponentProcessingStep.java
@@ -26,8 +26,9 @@
 import androidx.room.compiler.processing.XMessager;
 import androidx.room.compiler.processing.XTypeElement;
 import com.google.auto.common.BasicAnnotationProcessor.ProcessingStep;
+import com.google.common.base.Supplier;
+import com.google.common.base.Suppliers;
 import com.google.common.collect.ImmutableSet;
-import com.google.errorprone.annotations.CanIgnoreReturnValue;
 import com.squareup.javapoet.ClassName;
 import dagger.internal.codegen.base.SourceFileGenerator;
 import dagger.internal.codegen.binding.BindingGraph;
@@ -103,11 +104,19 @@
     if (!isValid(componentDescriptor)) {
       return;
     }
-    if (!validateFullBindingGraph(componentDescriptor)) {
-      return;
+
+    Supplier<dagger.spi.model.BindingGraph> fullBindingGraphSupplier =
+        Suppliers.memoize(
+            () -> bindingGraphFactory.create(componentDescriptor, true).topLevelBindingGraph());
+    if (bindingGraphValidator.shouldDoFullBindingGraphValidation(component)) {
+      if (!bindingGraphValidator.isValid(fullBindingGraphSupplier.get())) {
+        return;
+      }
     }
+
     BindingGraph bindingGraph = bindingGraphFactory.create(componentDescriptor, false);
-    if (bindingGraphValidator.isValid(bindingGraph.topLevelBindingGraph())) {
+    if (bindingGraphValidator.isValid(
+        bindingGraph.topLevelBindingGraph(), fullBindingGraphSupplier)) {
       generateComponent(bindingGraph);
     }
   }
@@ -116,10 +125,14 @@
     if (!isComponentValid(subcomponent)) {
       return;
     }
+    // TODO(dpb): ComponentDescriptorValidator for subcomponents, as we do for root components.
     ComponentDescriptor subcomponentDescriptor =
         componentDescriptorFactory.subcomponentDescriptor(subcomponent);
-    // TODO(dpb): ComponentDescriptorValidator for subcomponents, as we do for root components.
-    validateFullBindingGraph(subcomponentDescriptor);
+    if (!bindingGraphValidator.shouldDoFullBindingGraphValidation(subcomponent)) {
+      return;
+    }
+    BindingGraph fullBindingGraph = bindingGraphFactory.create(subcomponentDescriptor, true);
+    boolean isValid = bindingGraphValidator.isValid(fullBindingGraph.topLevelBindingGraph());
   }
 
   private void generateComponent(BindingGraph bindingGraph) {
@@ -136,16 +149,6 @@
     return report.isClean();
   }
 
-  @CanIgnoreReturnValue
-  private boolean validateFullBindingGraph(ComponentDescriptor componentDescriptor) {
-    if (!bindingGraphValidator.shouldDoFullBindingGraphValidation(
-        componentDescriptor.typeElement())) {
-      return true;
-    }
-    BindingGraph fullBindingGraph = bindingGraphFactory.create(componentDescriptor, true);
-    return bindingGraphValidator.isValid(fullBindingGraph.topLevelBindingGraph());
-  }
-
   private boolean isValid(ComponentDescriptor componentDescriptor) {
     ValidationReport componentDescriptorReport =
         componentDescriptorValidator.validate(componentDescriptor);
diff --git a/java/dagger/internal/codegen/validation/BindingGraphValidator.java b/java/dagger/internal/codegen/validation/BindingGraphValidator.java
index 09477d8..d20fe99 100644
--- a/java/dagger/internal/codegen/validation/BindingGraphValidator.java
+++ b/java/dagger/internal/codegen/validation/BindingGraphValidator.java
@@ -17,6 +17,8 @@
 package dagger.internal.codegen.validation;
 
 import androidx.room.compiler.processing.XTypeElement;
+import com.google.common.base.Optional;
+import com.google.common.base.Supplier;
 import dagger.internal.codegen.compileroption.CompilerOptions;
 import dagger.internal.codegen.compileroption.ValidationType;
 import dagger.spi.model.BindingGraph;
@@ -51,17 +53,23 @@
   }
 
   /** Returns {@code true} if no errors are reported for {@code graph}. */
-  public boolean isValid(BindingGraph graph) {
-    return visitValidationPlugins(graph) && visitExternalPlugins(graph);
+  public boolean isValid(BindingGraph fullGraph) {
+    return visitValidationPlugins(Optional.absent(), () -> fullGraph)
+        && visitExternalPlugins(fullGraph);
+  }
+
+  public boolean isValid(BindingGraph prunedGraph, Supplier<BindingGraph> fullGraphSupplier) {
+    return visitValidationPlugins(Optional.of(prunedGraph), fullGraphSupplier)
+        && visitExternalPlugins(prunedGraph);
   }
 
   /** Returns {@code true} if validation plugins report no errors. */
-  private boolean visitValidationPlugins(BindingGraph graph) {
-    if (graph.isFullBindingGraph() && !requiresFullBindingGraphValidation()) {
+  private boolean visitValidationPlugins(
+    Optional<BindingGraph> prunedGraph, Supplier<BindingGraph> fullGraphSupplier) {
+    if (!prunedGraph.isPresent() && !requiresFullBindingGraphValidation()) {
       return true;
     }
-
-    return validationPlugins.visit(graph);
+    return validationPlugins.visit(prunedGraph, fullGraphSupplier);
   }
 
   /** Returns {@code true} if external plugins report no errors. */
diff --git a/java/dagger/internal/codegen/validation/ValidationBindingGraphPlugin.java b/java/dagger/internal/codegen/validation/ValidationBindingGraphPlugin.java
new file mode 100644
index 0000000..e431069
--- /dev/null
+++ b/java/dagger/internal/codegen/validation/ValidationBindingGraphPlugin.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2023 The Dagger Authors.
+ *
+ * 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 dagger.internal.codegen.validation;
+
+import com.google.common.base.Preconditions;
+import dagger.spi.model.BindingGraph;
+import dagger.spi.model.BindingGraph.ComponentNode;
+import dagger.spi.model.BindingGraphPlugin;
+import dagger.spi.model.DiagnosticReporter;
+import java.util.HashSet;
+import java.util.Set;
+
+/** BindingGraphPlugin that allows rerun visitGraph on full binding graph. */
+public abstract class ValidationBindingGraphPlugin implements BindingGraphPlugin {
+  private final Set<ComponentNode> visitFullGraphRequested = new HashSet<>();
+
+  public final boolean visitFullGraphRequested(BindingGraph graph) {
+    return visitFullGraphRequested.contains(graph.rootComponentNode());
+  }
+
+  /**
+   * Request revisit full binding graph for the given pruned graph.
+   *
+   * <p>If called from revisitFullGraph then no-op.
+   */
+  public final void requestVisitFullGraph(BindingGraph graph) {
+    Preconditions.checkState(
+        !graph.isFullBindingGraph(), "Cannot request revisit full graph when visiting full graph.");
+    visitFullGraphRequested.add(graph.rootComponentNode());
+  }
+
+  public void revisitFullGraph(
+      BindingGraph prunedGraph, BindingGraph fullGraph, DiagnosticReporter diagReporter) {}
+}
diff --git a/java/dagger/internal/codegen/validation/ValidationBindingGraphPlugins.java b/java/dagger/internal/codegen/validation/ValidationBindingGraphPlugins.java
index a283b99..9e2bbf4 100644
--- a/java/dagger/internal/codegen/validation/ValidationBindingGraphPlugins.java
+++ b/java/dagger/internal/codegen/validation/ValidationBindingGraphPlugins.java
@@ -20,6 +20,8 @@
 import static javax.tools.Diagnostic.Kind.ERROR;
 
 import androidx.room.compiler.processing.XProcessingEnv;
+import com.google.common.base.Optional;
+import com.google.common.base.Supplier;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Maps;
@@ -30,13 +32,15 @@
 import dagger.spi.model.BindingGraph;
 import dagger.spi.model.BindingGraphPlugin;
 import dagger.spi.model.DaggerProcessingEnv;
+import java.util.ArrayList;
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import javax.inject.Inject;
 
 /** Initializes {@link BindingGraphPlugin}s. */
 public final class ValidationBindingGraphPlugins {
-  private final ImmutableSet<BindingGraphPlugin> plugins;
+  private final ImmutableSet<ValidationBindingGraphPlugin> plugins;
   private final DiagnosticReporterFactory diagnosticReporterFactory;
   private final XProcessingEnv processingEnv;
   private final CompilerOptions compilerOptions;
@@ -44,7 +48,7 @@
 
   @Inject
   ValidationBindingGraphPlugins(
-      @Validation ImmutableSet<BindingGraphPlugin> plugins,
+      @Validation ImmutableSet<ValidationBindingGraphPlugin> plugins,
       DiagnosticReporterFactory diagnosticReporterFactory,
       XProcessingEnv processingEnv,
       CompilerOptions compilerOptions,
@@ -79,25 +83,43 @@
   }
 
   /** Returns {@code false} if any of the plugins reported an error. */
-  boolean visit(BindingGraph graph) {
-    boolean errorsAsWarnings =
-        graph.isFullBindingGraph()
-            && compilerOptions.fullBindingGraphValidationType().equals(ValidationType.WARNING);
+  boolean visit(Optional<BindingGraph> prunedGraph, Supplier<BindingGraph> fullGraphSupplier) {
+    BindingGraph graph = prunedGraph.isPresent() ? prunedGraph.get() : fullGraphSupplier.get();
 
     boolean isClean = true;
-    for (BindingGraphPlugin plugin : plugins) {
-      DiagnosticReporterImpl reporter =
-          errorsAsWarnings
-              ? diagnosticReporterFactory.reporterWithErrorAsWarnings(graph, plugin.pluginName())
-              : diagnosticReporterFactory.reporter(graph, plugin.pluginName());
+    List<ValidationBindingGraphPlugin> rerunPlugins = new ArrayList<>();
+    for (ValidationBindingGraphPlugin plugin : plugins) {
+      DiagnosticReporterImpl reporter = createReporter(plugin.pluginName(), graph);
       plugin.visitGraph(graph, reporter);
+      if (plugin.visitFullGraphRequested(graph)) {
+        rerunPlugins.add(plugin);
+      }
       if (reporter.reportedDiagnosticKinds().contains(ERROR)) {
         isClean = false;
       }
     }
+    if (!rerunPlugins.isEmpty()) {
+      BindingGraph fullGraph = fullGraphSupplier.get();
+      for (ValidationBindingGraphPlugin plugin : rerunPlugins) {
+        DiagnosticReporterImpl reporter = createReporter(plugin.pluginName(), fullGraph);
+        plugin.revisitFullGraph(prunedGraph.get(), fullGraph, reporter);
+        if (reporter.reportedDiagnosticKinds().contains(ERROR)) {
+          isClean = false;
+        }
+      }
+    }
     return isClean;
   }
 
+  private DiagnosticReporterImpl createReporter(String pluginName, BindingGraph graph) {
+    boolean errorsAsWarnings =
+        graph.isFullBindingGraph()
+            && compilerOptions.fullBindingGraphValidationType().equals(ValidationType.WARNING);
+    return errorsAsWarnings
+        ? diagnosticReporterFactory.reporterWithErrorAsWarnings(graph, pluginName)
+        : diagnosticReporterFactory.reporter(graph, pluginName);
+  }
+
   public void endPlugins() {
     plugins.forEach(BindingGraphPlugin::onPluginEnd);
   }