• 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.base.RequestKinds.dependencyCanBeProduction;
23 import static dagger.internal.codegen.extension.DaggerStreams.instancesOf;
24 import static javax.tools.Diagnostic.Kind.ERROR;
25 
26 import dagger.internal.codegen.model.Binding;
27 import dagger.internal.codegen.model.BindingGraph;
28 import dagger.internal.codegen.model.BindingGraph.DependencyEdge;
29 import dagger.internal.codegen.model.BindingGraph.Node;
30 import dagger.internal.codegen.model.DiagnosticReporter;
31 import dagger.internal.codegen.validation.ValidationBindingGraphPlugin;
32 import java.util.stream.Stream;
33 import javax.inject.Inject;
34 
35 /**
36  * Reports an error for each provision-only dependency request that is satisfied by a production
37  * binding.
38  */
39 // TODO(b/29509141): Clarify the error.
40 final class ProvisionDependencyOnProducerBindingValidator extends ValidationBindingGraphPlugin {
41 
42   @Inject
ProvisionDependencyOnProducerBindingValidator()43   ProvisionDependencyOnProducerBindingValidator() {}
44 
45   @Override
pluginName()46   public String pluginName() {
47     return "Dagger/ProviderDependsOnProducer";
48   }
49 
50   @Override
visitGraph(BindingGraph bindingGraph, DiagnosticReporter diagnosticReporter)51   public void visitGraph(BindingGraph bindingGraph, DiagnosticReporter diagnosticReporter) {
52     provisionDependenciesOnProductionBindings(bindingGraph)
53         .forEach(
54             provisionDependent ->
55                 diagnosticReporter.reportDependency(
56                     ERROR,
57                     provisionDependent,
58                     provisionDependent.isEntryPoint()
59                         ? entryPointErrorMessage(provisionDependent)
60                         : dependencyErrorMessage(provisionDependent, bindingGraph)));
61   }
62 
provisionDependenciesOnProductionBindings( BindingGraph bindingGraph)63   private Stream<DependencyEdge> provisionDependenciesOnProductionBindings(
64       BindingGraph bindingGraph) {
65     return bindingGraph.bindings().stream()
66         .filter(binding -> binding.isProduction())
67         .flatMap(binding -> incomingDependencies(binding, bindingGraph))
68         .filter(edge -> !dependencyCanBeProduction(edge, bindingGraph));
69   }
70 
71   /** Returns the dependencies on {@code binding}. */
72   // TODO(dpb): Move to BindingGraph.
incomingDependencies(Binding binding, BindingGraph bindingGraph)73   private Stream<DependencyEdge> incomingDependencies(Binding binding, BindingGraph bindingGraph) {
74     return bindingGraph.network().inEdges(binding).stream()
75         .flatMap(instancesOf(DependencyEdge.class));
76   }
77 
78   /**
79    * Returns the binding that requests a dependency.
80    *
81    * @throws IllegalArgumentException if {@code dependency} is an {@linkplain
82    *     DependencyEdge#isEntryPoint() entry point}.
83    */
84   // TODO(dpb): Move to BindingGraph.
bindingRequestingDependency( DependencyEdge dependency, BindingGraph bindingGraph)85   private Binding bindingRequestingDependency(
86       DependencyEdge dependency, BindingGraph bindingGraph) {
87     checkArgument(!dependency.isEntryPoint());
88     Node source = bindingGraph.network().incidentNodes(dependency).source();
89     verify(
90         source instanceof Binding,
91         "expected source of %s to be a binding, but was: %s",
92         dependency,
93         source);
94     return (Binding) source;
95   }
96 
entryPointErrorMessage(DependencyEdge entryPoint)97   private String entryPointErrorMessage(DependencyEdge entryPoint) {
98     return String.format(
99         "%s is a provision entry-point, which cannot depend on a production.",
100         entryPoint.dependencyRequest().key());
101   }
102 
dependencyErrorMessage( DependencyEdge dependencyOnProduction, BindingGraph bindingGraph)103   private String dependencyErrorMessage(
104       DependencyEdge dependencyOnProduction, BindingGraph bindingGraph) {
105     if (!canBeSatisfiedByProductionBinding(
106         dependencyOnProduction.dependencyRequest().kind(), false)) {
107       return String.format(
108           "request kind %s cannot be satisfied by production binding.",
109           dependencyOnProduction.dependencyRequest().kind());
110     }
111     return String.format(
112         "%s is a provision, which cannot depend on a production.",
113         bindingRequestingDependency(dependencyOnProduction, bindingGraph).key());
114   }
115 }
116