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.internal.codegen.model.Binding; 26 import dagger.internal.codegen.model.BindingGraph; 27 import dagger.internal.codegen.model.BindingGraph.DependencyEdge; 28 import dagger.internal.codegen.model.BindingGraph.Node; 29 import dagger.internal.codegen.model.DiagnosticReporter; 30 import dagger.internal.codegen.validation.ValidationBindingGraphPlugin; 31 import java.util.stream.Stream; 32 import javax.inject.Inject; 33 34 /** 35 * Reports an error for each provision-only dependency request that is satisfied by a production 36 * binding. 37 */ 38 // TODO(b/29509141): Clarify the error. 39 final class ProvisionDependencyOnProducerBindingValidator extends ValidationBindingGraphPlugin { 40 41 @Inject ProvisionDependencyOnProducerBindingValidator()42 ProvisionDependencyOnProducerBindingValidator() {} 43 44 @Override pluginName()45 public String pluginName() { 46 return "Dagger/ProviderDependsOnProducer"; 47 } 48 49 @Override visitGraph(BindingGraph bindingGraph, DiagnosticReporter diagnosticReporter)50 public void visitGraph(BindingGraph bindingGraph, DiagnosticReporter diagnosticReporter) { 51 provisionDependenciesOnProductionBindings(bindingGraph) 52 .forEach( 53 provisionDependent -> 54 diagnosticReporter.reportDependency( 55 ERROR, 56 provisionDependent, 57 provisionDependent.isEntryPoint() 58 ? entryPointErrorMessage(provisionDependent) 59 : dependencyErrorMessage(provisionDependent, bindingGraph))); 60 } 61 provisionDependenciesOnProductionBindings( BindingGraph bindingGraph)62 private Stream<DependencyEdge> provisionDependenciesOnProductionBindings( 63 BindingGraph bindingGraph) { 64 return bindingGraph.bindings().stream() 65 .filter(binding -> binding.isProduction()) 66 .flatMap(binding -> incomingDependencies(binding, bindingGraph)) 67 .filter(edge -> !dependencyCanUseProduction(edge, bindingGraph)); 68 } 69 70 /** Returns the dependencies on {@code binding}. */ 71 // TODO(dpb): Move to BindingGraph. incomingDependencies(Binding binding, BindingGraph bindingGraph)72 private Stream<DependencyEdge> incomingDependencies(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 Binding bindingRequestingDependency( 92 DependencyEdge dependency, BindingGraph bindingGraph) { 93 checkArgument(!dependency.isEntryPoint()); 94 Node source = bindingGraph.network().incidentNodes(dependency).source(); 95 verify( 96 source instanceof Binding, 97 "expected source of %s to be a binding, but was: %s", 98 dependency, 99 source); 100 return (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