• 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.internal.codegen.model.BindingKind.DELEGATE;
20 import static dagger.internal.codegen.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.internal.codegen.model.Binding;
29 import dagger.internal.codegen.model.BindingGraph;
30 import dagger.internal.codegen.model.DiagnosticReporter;
31 import dagger.internal.codegen.model.Key;
32 import dagger.internal.codegen.validation.ValidationBindingGraphPlugin;
33 import java.util.Optional;
34 import javax.inject.Inject;
35 
36 /** Validates that there are not multiple set binding contributions to the same binding. */
37 final class SetMultibindingValidator extends ValidationBindingGraphPlugin {
38 
39   @Inject
SetMultibindingValidator()40   SetMultibindingValidator() {
41   }
42 
43   @Override
pluginName()44   public String pluginName() {
45     return "Dagger/SetMultibinding";
46   }
47 
48   @Override
visitGraph(BindingGraph bindingGraph, DiagnosticReporter diagnosticReporter)49   public void visitGraph(BindingGraph bindingGraph, DiagnosticReporter diagnosticReporter) {
50     bindingGraph.bindings().stream()
51         .filter(binding -> binding.kind().equals(MULTIBOUND_SET))
52         .forEach(
53             binding ->
54                 checkForDuplicateSetContributions(binding, bindingGraph, diagnosticReporter));
55   }
56 
checkForDuplicateSetContributions( Binding binding, BindingGraph bindingGraph, DiagnosticReporter diagnosticReporter)57   private void checkForDuplicateSetContributions(
58       Binding binding, BindingGraph bindingGraph, DiagnosticReporter diagnosticReporter) {
59     // Map of delegate target key to the original contribution binding
60     Multimap<Key, Binding> dereferencedBindsTargets = HashMultimap.create();
61     for (Binding dep : bindingGraph.requestedBindings(binding)) {
62       if (dep.kind().equals(DELEGATE)) {
63         dereferenceDelegateBinding(dep, bindingGraph)
64             .ifPresent(dereferencedKey -> dereferencedBindsTargets.put(dereferencedKey, dep));
65       }
66     }
67 
68     dereferencedBindsTargets
69         .asMap()
70         .forEach(
71             (targetKey, contributions) -> {
72               if (contributions.size() > 1) {
73                 diagnosticReporter.reportComponent(
74                     ERROR,
75                     bindingGraph.componentNode(binding.componentPath()).get(),
76                     "Multiple set contributions into %s for the same contribution key: %s.\n\n"
77                         + "    %s\n",
78                     binding.key(),
79                     targetKey,
80                     Joiner.on("\n    ").join(contributions));
81               }
82             });
83   }
84 
85   /**
86    * Returns the dereferenced key of a delegate binding (going through other delegates as well).
87    *
88    * <p>If the binding cannot be dereferenced (because it leads to a missing binding or duplicate
89    * bindings) then {@link Optional#empty()} is returned.
90    */
dereferenceDelegateBinding(Binding binding, BindingGraph bindingGraph)91   private Optional<Key> dereferenceDelegateBinding(Binding binding, BindingGraph bindingGraph) {
92     ImmutableSet<Binding> delegateSet = bindingGraph.requestedBindings(binding);
93     if (delegateSet.size() != 1) {
94       // If there isn't exactly 1 delegate then it means either a MissingBinding or DuplicateBinding
95       // error will be reported. Just return nothing rather than trying to dereference further, as
96       // anything we report here will just be noise on top of the other error anyway.
97       return Optional.empty();
98     }
99     // If there is a binding, first we check if that is a delegate binding so we can dereference
100     // that binding if needed.
101     Binding delegate = Iterables.getOnlyElement(delegateSet);
102     if (delegate.kind().equals(DELEGATE)) {
103       return dereferenceDelegateBinding(delegate, bindingGraph);
104     }
105     return Optional.of(delegate.key());
106   }
107 }
108