• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 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 dagger.spi.model.BindingKind.DELEGATE;
20 import static dagger.spi.model.BindingKind.MULTIBOUND_SET;
21 import static javax.tools.Diagnostic.Kind.ERROR;
22 
23 import com.google.common.base.Joiner;
24 import com.google.common.collect.HashMultimap;
25 import com.google.common.collect.ImmutableSet;
26 import com.google.common.collect.Iterables;
27 import com.google.common.collect.Multimap;
28 import dagger.spi.model.Binding;
29 import dagger.spi.model.BindingGraph;
30 import dagger.spi.model.BindingGraphPlugin;
31 import dagger.spi.model.DiagnosticReporter;
32 import dagger.spi.model.Key;
33 import javax.inject.Inject;
34 
35 /** Validates that there are not multiple set binding contributions to the same binding. */
36 final class SetMultibindingValidator implements BindingGraphPlugin {
37 
38   @Inject
SetMultibindingValidator()39   SetMultibindingValidator() {
40   }
41 
42   @Override
pluginName()43   public String pluginName() {
44     return "Dagger/SetMultibinding";
45   }
46 
47   @Override
visitGraph(BindingGraph bindingGraph, DiagnosticReporter diagnosticReporter)48   public void visitGraph(BindingGraph bindingGraph, DiagnosticReporter diagnosticReporter) {
49     bindingGraph.bindings().stream()
50         .filter(binding -> binding.kind().equals(MULTIBOUND_SET))
51         .forEach(
52             binding ->
53                 checkForDuplicateSetContributions(binding, bindingGraph, diagnosticReporter));
54   }
55 
checkForDuplicateSetContributions( Binding binding, BindingGraph bindingGraph, DiagnosticReporter diagnosticReporter)56   private void checkForDuplicateSetContributions(
57       Binding binding, BindingGraph bindingGraph, DiagnosticReporter diagnosticReporter) {
58     // Map of delegate target key to the original contribution binding
59     Multimap<Key, Binding> dereferencedBindsTargets = HashMultimap.create();
60     for (Binding dep : bindingGraph.requestedBindings(binding)) {
61       if (dep.kind().equals(DELEGATE)) {
62         dereferencedBindsTargets.put(dereferenceDelegateBinding(dep, bindingGraph), dep);
63       }
64     }
65 
66     dereferencedBindsTargets
67         .asMap()
68         .forEach(
69             (targetKey, contributions) -> {
70               if (contributions.size() > 1) {
71                 diagnosticReporter.reportComponent(
72                     ERROR,
73                     bindingGraph.componentNode(binding.componentPath()).get(),
74                     "Multiple set contributions into %s for the same contribution key: %s.\n\n"
75                         + "    %s\n",
76                     binding.key(),
77                     targetKey,
78                     Joiner.on("\n    ").join(contributions));
79               }
80             });
81   }
82 
83   /** Returns the delegate target of a delegate binding (going through other delegates as well). */
dereferenceDelegateBinding(Binding binding, BindingGraph bindingGraph)84   private Key dereferenceDelegateBinding(Binding binding, BindingGraph bindingGraph) {
85     ImmutableSet<Binding> delegateSet = bindingGraph.requestedBindings(binding);
86     if (delegateSet.isEmpty()) {
87       // There may not be a delegate if the delegate is missing. In this case, we just take the
88       // requested key and return that.
89       return Iterables.getOnlyElement(binding.dependencies()).key();
90     }
91     // If there is a binding, first we check if that is a delegate binding so we can dereference
92     // that binding if needed.
93     Binding delegate = Iterables.getOnlyElement(delegateSet);
94     if (delegate.kind().equals(DELEGATE)) {
95       return dereferenceDelegateBinding(delegate, bindingGraph);
96     }
97     return delegate.key();
98   }
99 }
100