• 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.validation;
18 
19 import static com.google.common.collect.Lists.asList;
20 import static dagger.internal.codegen.base.ElementFormatter.elementToString;
21 import static dagger.internal.codegen.langmodel.DaggerElements.transitivelyEncloses;
22 import static javax.tools.Diagnostic.Kind.ERROR;
23 
24 import androidx.room.compiler.processing.XElement;
25 import androidx.room.compiler.processing.XMessager;
26 import androidx.room.compiler.processing.XTypeElement;
27 import com.google.common.collect.ImmutableSet;
28 import com.google.errorprone.annotations.FormatMethod;
29 import dagger.spi.model.BindingGraph;
30 import dagger.spi.model.BindingGraph.ChildFactoryMethodEdge;
31 import dagger.spi.model.BindingGraph.ComponentNode;
32 import dagger.spi.model.BindingGraph.DependencyEdge;
33 import dagger.spi.model.BindingGraph.MaybeBinding;
34 import dagger.spi.model.DiagnosticReporter;
35 import javax.inject.Inject;
36 import javax.tools.Diagnostic;
37 import org.checkerframework.checker.nullness.compatqual.NullableDecl;
38 
39 /** A factory for {@link DiagnosticReporter}s. */
40 // TODO(ronshapiro): If multiple plugins print errors on the same node/edge, should we condense the
41 // messages and only print the dependency trace once?
42 final class DiagnosticReporterFactory {
43   private final XMessager messager;
44   private final DiagnosticMessageGenerator.Factory diagnosticMessageGeneratorFactory;
45 
46   @Inject
DiagnosticReporterFactory( XMessager messager, DiagnosticMessageGenerator.Factory diagnosticMessageGeneratorFactory)47   DiagnosticReporterFactory(
48       XMessager messager, DiagnosticMessageGenerator.Factory diagnosticMessageGeneratorFactory) {
49     this.messager = messager;
50     this.diagnosticMessageGeneratorFactory = diagnosticMessageGeneratorFactory;
51   }
52 
53   /** Creates a reporter for a binding graph and a plugin. */
reporter( BindingGraph graph, String pluginName, boolean reportErrorsAsWarnings)54   DiagnosticReporterImpl reporter(
55       BindingGraph graph, String pluginName, boolean reportErrorsAsWarnings) {
56     return new DiagnosticReporterImpl(graph, pluginName, reportErrorsAsWarnings);
57   }
58 
59   /**
60    * A {@link DiagnosticReporter} that keeps track of which {@linkplain Diagnostic.Kind kinds} of
61    * diagnostics were reported.
62    */
63   final class DiagnosticReporterImpl implements DiagnosticReporter {
64     private final String plugin;
65     private final XTypeElement rootComponent;
66     private final boolean reportErrorsAsWarnings;
67     private final ImmutableSet.Builder<Diagnostic.Kind> reportedDiagnosticKinds =
68         ImmutableSet.builder();
69     private final DiagnosticMessageGenerator diagnosticMessageGenerator;
70 
DiagnosticReporterImpl(BindingGraph graph, String plugin, boolean reportErrorsAsWarnings)71     DiagnosticReporterImpl(BindingGraph graph, String plugin, boolean reportErrorsAsWarnings) {
72       this.plugin = plugin;
73       this.reportErrorsAsWarnings = reportErrorsAsWarnings;
74       this.rootComponent =
75           graph.rootComponentNode().componentPath().currentComponent().xprocessing();
76       this.diagnosticMessageGenerator = diagnosticMessageGeneratorFactory.create(graph);
77     }
78 
79     /** Returns which {@linkplain Diagnostic.Kind kinds} of diagnostics were reported. */
reportedDiagnosticKinds()80     ImmutableSet<Diagnostic.Kind> reportedDiagnosticKinds() {
81       return reportedDiagnosticKinds.build();
82     }
83 
84     @Override
reportComponent( Diagnostic.Kind diagnosticKind, ComponentNode componentNode, String messageFormat)85     public void reportComponent(
86         Diagnostic.Kind diagnosticKind, ComponentNode componentNode, String messageFormat) {
87       StringBuilder message = new StringBuilder(messageFormat);
88       diagnosticMessageGenerator.appendComponentPathUnlessAtRoot(message, componentNode);
89       // TODO(dpb): Report at the component node component.
90       printMessage(diagnosticKind, message, rootComponent);
91     }
92 
93     @Override
94     @FormatMethod
reportComponent( Diagnostic.Kind diagnosticKind, ComponentNode componentNode, String messageFormat, Object firstArg, Object... moreArgs)95     public void reportComponent(
96         Diagnostic.Kind diagnosticKind,
97         ComponentNode componentNode,
98         String messageFormat,
99         Object firstArg,
100         Object... moreArgs) {
101       reportComponent(
102           diagnosticKind, componentNode, formatMessage(messageFormat, firstArg, moreArgs));
103     }
104 
105     // TODO(ronshapiro): should this also include the binding element?
106     @Override
reportBinding( Diagnostic.Kind diagnosticKind, MaybeBinding binding, String message)107     public void reportBinding(
108         Diagnostic.Kind diagnosticKind, MaybeBinding binding, String message) {
109       printMessage(
110           diagnosticKind, message + diagnosticMessageGenerator.getMessage(binding), rootComponent);
111     }
112 
113     @Override
reportBinding( Diagnostic.Kind diagnosticKind, MaybeBinding binding, String messageFormat, Object firstArg, Object... moreArgs)114     public void reportBinding(
115         Diagnostic.Kind diagnosticKind,
116         MaybeBinding binding,
117         String messageFormat,
118         Object firstArg,
119         Object... moreArgs) {
120       reportBinding(diagnosticKind, binding, formatMessage(messageFormat, firstArg, moreArgs));
121     }
122 
123     @Override
reportDependency( Diagnostic.Kind diagnosticKind, DependencyEdge dependencyEdge, String message)124     public void reportDependency(
125         Diagnostic.Kind diagnosticKind, DependencyEdge dependencyEdge, String message) {
126       printMessage(
127           diagnosticKind,
128           message + diagnosticMessageGenerator.getMessage(dependencyEdge),
129           rootComponent);
130     }
131 
132     @Override
reportDependency( Diagnostic.Kind diagnosticKind, DependencyEdge dependencyEdge, String messageFormat, Object firstArg, Object... moreArgs)133     public void reportDependency(
134         Diagnostic.Kind diagnosticKind,
135         DependencyEdge dependencyEdge,
136         String messageFormat,
137         Object firstArg,
138         Object... moreArgs) {
139       reportDependency(
140           diagnosticKind, dependencyEdge, formatMessage(messageFormat, firstArg, moreArgs));
141     }
142 
143     @Override
reportSubcomponentFactoryMethod( Diagnostic.Kind diagnosticKind, ChildFactoryMethodEdge childFactoryMethodEdge, String message)144     public void reportSubcomponentFactoryMethod(
145         Diagnostic.Kind diagnosticKind,
146         ChildFactoryMethodEdge childFactoryMethodEdge,
147         String message) {
148       printMessage(diagnosticKind, message, childFactoryMethodEdge.factoryMethod().xprocessing());
149     }
150 
151     @Override
reportSubcomponentFactoryMethod( Diagnostic.Kind diagnosticKind, ChildFactoryMethodEdge childFactoryMethodEdge, String messageFormat, Object firstArg, Object... moreArgs)152     public void reportSubcomponentFactoryMethod(
153         Diagnostic.Kind diagnosticKind,
154         ChildFactoryMethodEdge childFactoryMethodEdge,
155         String messageFormat,
156         Object firstArg,
157         Object... moreArgs) {
158       reportSubcomponentFactoryMethod(
159           diagnosticKind, childFactoryMethodEdge, formatMessage(messageFormat, firstArg, moreArgs));
160     }
161 
formatMessage(String messageFormat, Object firstArg, Object[] moreArgs)162     private String formatMessage(String messageFormat, Object firstArg, Object[] moreArgs) {
163       return String.format(messageFormat, asList(firstArg, moreArgs).toArray());
164     }
165 
printMessage( Diagnostic.Kind diagnosticKind, CharSequence message, @NullableDecl XElement elementToReport)166     private void printMessage(
167         Diagnostic.Kind diagnosticKind,
168         CharSequence message,
169         @NullableDecl XElement elementToReport) {
170       if (diagnosticKind.equals(ERROR) && reportErrorsAsWarnings) {
171         diagnosticKind = Diagnostic.Kind.WARNING;
172       }
173       reportedDiagnosticKinds.add(diagnosticKind);
174       StringBuilder fullMessage = new StringBuilder();
175       appendBracketPrefix(fullMessage, plugin);
176 
177       if (elementToReport == null) {
178         messager.printMessage(diagnosticKind, fullMessage.append(message).toString());
179       } else {
180         if (!transitivelyEncloses(rootComponent, elementToReport)) {
181           appendBracketPrefix(fullMessage, elementToString(elementToReport));
182           elementToReport = rootComponent;
183         }
184         messager.printMessage(
185             diagnosticKind, fullMessage.append(message).toString(), elementToReport);
186       }
187     }
188 
appendBracketPrefix(StringBuilder message, String prefix)189     private void appendBracketPrefix(StringBuilder message, String prefix) {
190       message.append(String.format("[%s] ", prefix));
191     }
192   }
193 }
194