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