• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 The Dagger Authors.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package dagger.internal.codegen.bindinggraphvalidation;
18 
19 import static com.google.common.base.Preconditions.checkArgument;
20 import static com.google.common.base.Verify.verify;
21 import static dagger.internal.codegen.base.RequestKinds.canBeSatisfiedByProductionBinding;
22 import static dagger.internal.codegen.extension.DaggerStreams.instancesOf;
23 import static javax.tools.Diagnostic.Kind.ERROR;
24 
25 import dagger.model.BindingGraph;
26 import dagger.model.BindingGraph.DependencyEdge;
27 import dagger.model.BindingGraph.Node;
28 import dagger.spi.BindingGraphPlugin;
29 import dagger.spi.DiagnosticReporter;
30 import java.util.stream.Stream;
31 import javax.inject.Inject;
32 
33 /**
34  * Reports an error for each provision-only dependency request that is satisfied by a production
35  * binding.
36  */
37 // TODO(b/29509141): Clarify the error.
38 final class ProvisionDependencyOnProducerBindingValidator implements BindingGraphPlugin {
39 
40   @Inject
ProvisionDependencyOnProducerBindingValidator()41   ProvisionDependencyOnProducerBindingValidator() {}
42 
43   @Override
pluginName()44   public String pluginName() {
45     return "Dagger/ProviderDependsOnProducer";
46   }
47 
48   @Override
visitGraph(BindingGraph bindingGraph, DiagnosticReporter diagnosticReporter)49   public void visitGraph(BindingGraph bindingGraph, DiagnosticReporter diagnosticReporter) {
50     provisionDependenciesOnProductionBindings(bindingGraph)
51         .forEach(
52             provisionDependent ->
53                 diagnosticReporter.reportDependency(
54                     ERROR,
55                     provisionDependent,
56                     provisionDependent.isEntryPoint()
57                         ? entryPointErrorMessage(provisionDependent)
58                         : dependencyErrorMessage(provisionDependent, bindingGraph)));
59   }
60 
provisionDependenciesOnProductionBindings( BindingGraph bindingGraph)61   private Stream<DependencyEdge> provisionDependenciesOnProductionBindings(
62       BindingGraph bindingGraph) {
63     return bindingGraph.bindings().stream()
64         .filter(binding -> binding.isProduction())
65         .flatMap(binding -> incomingDependencies(binding, bindingGraph))
66         .filter(edge -> !dependencyCanUseProduction(edge, bindingGraph));
67   }
68 
69   /** Returns the dependencies on {@code binding}. */
70   // TODO(dpb): Move to BindingGraph.
incomingDependencies( dagger.model.Binding binding, BindingGraph bindingGraph)71   private Stream<DependencyEdge> incomingDependencies(
72       dagger.model.Binding binding, BindingGraph bindingGraph) {
73     return bindingGraph.network().inEdges(binding).stream()
74         .flatMap(instancesOf(DependencyEdge.class));
75   }
76 
77   // TODO(ronshapiro): merge with MissingBindingValidator.dependencyCanUseProduction
dependencyCanUseProduction(DependencyEdge edge, BindingGraph bindingGraph)78   private boolean dependencyCanUseProduction(DependencyEdge edge, BindingGraph bindingGraph) {
79     return edge.isEntryPoint()
80         ? canBeSatisfiedByProductionBinding(edge.dependencyRequest().kind())
81         : bindingRequestingDependency(edge, bindingGraph).isProduction();
82   }
83 
84   /**
85    * Returns the binding that requests a dependency.
86    *
87    * @throws IllegalArgumentException if {@code dependency} is an {@linkplain
88    *     DependencyEdge#isEntryPoint() entry point}.
89    */
90   // TODO(dpb): Move to BindingGraph.
bindingRequestingDependency( DependencyEdge dependency, BindingGraph bindingGraph)91   private dagger.model.Binding bindingRequestingDependency(
92       DependencyEdge dependency, BindingGraph bindingGraph) {
93     checkArgument(!dependency.isEntryPoint());
94     Node source = bindingGraph.network().incidentNodes(dependency).source();
95     verify(
96         source instanceof dagger.model.Binding,
97         "expected source of %s to be a binding, but was: %s",
98         dependency,
99         source);
100     return (dagger.model.Binding) source;
101   }
102 
entryPointErrorMessage(DependencyEdge entryPoint)103   private String entryPointErrorMessage(DependencyEdge entryPoint) {
104     return String.format(
105         "%s is a provision entry-point, which cannot depend on a production.",
106         entryPoint.dependencyRequest().key());
107   }
108 
dependencyErrorMessage( DependencyEdge dependencyOnProduction, BindingGraph bindingGraph)109   private String dependencyErrorMessage(
110       DependencyEdge dependencyOnProduction, BindingGraph bindingGraph) {
111     return String.format(
112         "%s is a provision, which cannot depend on a production.",
113         bindingRequestingDependency(dependencyOnProduction, bindingGraph).key());
114   }
115 }
116