285 lines
11 KiB
Java
285 lines
11 KiB
Java
/*
|
|
* Copyright (C) 2019 The Dagger Authors.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
package dagger.internal.codegen.validation;
|
|
|
|
import static com.google.common.base.Preconditions.checkArgument;
|
|
import static com.google.common.base.Preconditions.checkNotNull;
|
|
import static com.google.common.base.Preconditions.checkState;
|
|
import static com.google.common.collect.Lists.asList;
|
|
import static dagger.internal.codegen.base.ElementFormatter.elementToString;
|
|
import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet;
|
|
import static dagger.internal.codegen.langmodel.DaggerElements.elementEncloses;
|
|
|
|
import com.google.common.collect.ImmutableSet;
|
|
import com.google.errorprone.annotations.FormatMethod;
|
|
import dagger.model.BindingGraph;
|
|
import dagger.model.BindingGraph.ChildFactoryMethodEdge;
|
|
import dagger.model.BindingGraph.ComponentNode;
|
|
import dagger.model.BindingGraph.DependencyEdge;
|
|
import dagger.model.BindingGraph.MaybeBinding;
|
|
import dagger.spi.BindingGraphPlugin;
|
|
import dagger.spi.DiagnosticReporter;
|
|
import java.util.Map;
|
|
import java.util.Optional;
|
|
import java.util.Set;
|
|
import javax.annotation.processing.Filer;
|
|
import javax.inject.Inject;
|
|
import javax.lang.model.util.Elements; // ALLOW_TYPES_ELEMENTS because of interface dependencies
|
|
import javax.lang.model.util.Types; // ALLOW_TYPES_ELEMENTS because of interface dependencies
|
|
import javax.tools.Diagnostic;
|
|
|
|
/**
|
|
* Combines many {@link BindingGraphPlugin} implementations. This helps reduce spam by combining
|
|
* all of the messages that are reported on the root component.
|
|
*/
|
|
public final class CompositeBindingGraphPlugin implements BindingGraphPlugin {
|
|
|
|
private final ImmutableSet<BindingGraphPlugin> plugins;
|
|
private final String pluginName;
|
|
private final DiagnosticMessageGenerator.Factory messageGeneratorFactory;
|
|
|
|
/** Factory class for {@link CompositeBindingGraphPlugin}. */
|
|
public static final class Factory {
|
|
private final DiagnosticMessageGenerator.Factory messageGeneratorFactory;
|
|
|
|
@Inject Factory(DiagnosticMessageGenerator.Factory messageGeneratorFactory) {
|
|
this.messageGeneratorFactory = messageGeneratorFactory;
|
|
}
|
|
|
|
public CompositeBindingGraphPlugin create(
|
|
ImmutableSet<BindingGraphPlugin> plugins, String pluginName) {
|
|
return new CompositeBindingGraphPlugin(plugins, pluginName, messageGeneratorFactory);
|
|
}
|
|
}
|
|
|
|
private CompositeBindingGraphPlugin(
|
|
ImmutableSet<BindingGraphPlugin> plugins,
|
|
String pluginName,
|
|
DiagnosticMessageGenerator.Factory messageGeneratorFactory) {
|
|
this.plugins = plugins;
|
|
this.pluginName = pluginName;
|
|
this.messageGeneratorFactory = messageGeneratorFactory;
|
|
}
|
|
|
|
@Override
|
|
public void visitGraph(BindingGraph bindingGraph, DiagnosticReporter diagnosticReporter) {
|
|
AggregatingDiagnosticReporter aggregatingDiagnosticReporter = new AggregatingDiagnosticReporter(
|
|
bindingGraph, diagnosticReporter, messageGeneratorFactory.create(bindingGraph));
|
|
plugins.forEach(plugin -> {
|
|
aggregatingDiagnosticReporter.setCurrentPlugin(plugin.pluginName());
|
|
plugin.visitGraph(bindingGraph, aggregatingDiagnosticReporter);
|
|
});
|
|
aggregatingDiagnosticReporter.report();
|
|
}
|
|
|
|
@Override
|
|
public void initFiler(Filer filer) {
|
|
plugins.forEach(plugin -> plugin.initFiler(filer));
|
|
}
|
|
|
|
@Override
|
|
public void initTypes(Types types) {
|
|
plugins.forEach(plugin -> plugin.initTypes(types));
|
|
}
|
|
|
|
@Override
|
|
public void initElements(Elements elements) {
|
|
plugins.forEach(plugin -> plugin.initElements(elements));
|
|
}
|
|
|
|
@Override
|
|
public void initOptions(Map<String, String> options) {
|
|
plugins.forEach(plugin -> plugin.initOptions(options));
|
|
}
|
|
|
|
@Override
|
|
public Set<String> supportedOptions() {
|
|
return plugins.stream().flatMap(
|
|
plugin -> plugin.supportedOptions().stream()).collect(toImmutableSet());
|
|
}
|
|
|
|
@Override
|
|
public String pluginName() {
|
|
return pluginName;
|
|
}
|
|
|
|
// TODO(erichang): This kind of breaks some of the encapsulation by relying on or repeating
|
|
// logic within DiagnosticReporterImpl. Hopefully if the experiment for aggregated messages
|
|
// goes well though this can be merged with that implementation.
|
|
private static final class AggregatingDiagnosticReporter implements DiagnosticReporter {
|
|
private final DiagnosticReporter delegate;
|
|
private final BindingGraph graph;
|
|
// Initialize with a new line so the first message appears below the reported component
|
|
private final StringBuilder messageBuilder = new StringBuilder("\n");
|
|
private final DiagnosticMessageGenerator messageGenerator;
|
|
private Optional<Diagnostic.Kind> mergedDiagnosticKind = Optional.empty();
|
|
private String currentPluginName = null;
|
|
|
|
AggregatingDiagnosticReporter(
|
|
BindingGraph graph,
|
|
DiagnosticReporter delegate,
|
|
DiagnosticMessageGenerator messageGenerator) {
|
|
this.graph = graph;
|
|
this.delegate = delegate;
|
|
this.messageGenerator = messageGenerator;
|
|
}
|
|
|
|
/** Sets the currently running aggregated plugin. Used to add a diagnostic prefix. */
|
|
void setCurrentPlugin(String pluginName) {
|
|
currentPluginName = pluginName;
|
|
}
|
|
|
|
/** Reports all of the stored diagnostics. */
|
|
void report() {
|
|
if (mergedDiagnosticKind.isPresent()) {
|
|
delegate.reportComponent(
|
|
mergedDiagnosticKind.get(),
|
|
graph.rootComponentNode(),
|
|
PackageNameCompressor.compressPackagesInMessage(messageBuilder.toString()));
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void reportComponent(Diagnostic.Kind diagnosticKind, ComponentNode componentNode,
|
|
String message) {
|
|
addMessage(diagnosticKind, message);
|
|
messageGenerator.appendComponentPathUnlessAtRoot(messageBuilder, componentNode);
|
|
}
|
|
|
|
@Override
|
|
@FormatMethod
|
|
public void reportComponent(
|
|
Diagnostic.Kind diagnosticKind,
|
|
ComponentNode componentNode,
|
|
String messageFormat,
|
|
Object firstArg,
|
|
Object... moreArgs) {
|
|
reportComponent(
|
|
diagnosticKind, componentNode, formatMessage(messageFormat, firstArg, moreArgs));
|
|
}
|
|
|
|
@Override
|
|
public void reportBinding(Diagnostic.Kind diagnosticKind, MaybeBinding binding,
|
|
String message) {
|
|
addMessage(diagnosticKind,
|
|
String.format("%s%s", message, messageGenerator.getMessage(binding)));
|
|
}
|
|
|
|
@Override
|
|
@FormatMethod
|
|
public void reportBinding(
|
|
Diagnostic.Kind diagnosticKind,
|
|
MaybeBinding binding,
|
|
String messageFormat,
|
|
Object firstArg,
|
|
Object... moreArgs) {
|
|
reportBinding(diagnosticKind, binding, formatMessage(messageFormat, firstArg, moreArgs));
|
|
}
|
|
|
|
@Override
|
|
public void reportDependency(
|
|
Diagnostic.Kind diagnosticKind, DependencyEdge dependencyEdge, String message) {
|
|
addMessage(diagnosticKind,
|
|
String.format("%s%s", message, messageGenerator.getMessage(dependencyEdge)));
|
|
}
|
|
|
|
@Override
|
|
@FormatMethod
|
|
public void reportDependency(
|
|
Diagnostic.Kind diagnosticKind,
|
|
DependencyEdge dependencyEdge,
|
|
String messageFormat,
|
|
Object firstArg,
|
|
Object... moreArgs) {
|
|
reportDependency(
|
|
diagnosticKind, dependencyEdge, formatMessage(messageFormat, firstArg, moreArgs));
|
|
}
|
|
|
|
@Override
|
|
public void reportSubcomponentFactoryMethod(
|
|
Diagnostic.Kind diagnosticKind,
|
|
ChildFactoryMethodEdge childFactoryMethodEdge,
|
|
String message) {
|
|
// TODO(erichang): This repeats some of the logic in DiagnosticReporterImpl. Remove when
|
|
// merged.
|
|
if (elementEncloses(
|
|
graph.rootComponentNode().componentPath().currentComponent(),
|
|
childFactoryMethodEdge.factoryMethod())) {
|
|
// Let this pass through since it is not an error reported on the root component
|
|
delegate.reportSubcomponentFactoryMethod(diagnosticKind, childFactoryMethodEdge, message);
|
|
} else {
|
|
addMessage(
|
|
diagnosticKind,
|
|
String.format(
|
|
"[%s] %s", elementToString(childFactoryMethodEdge.factoryMethod()), message));
|
|
}
|
|
}
|
|
|
|
@Override
|
|
@FormatMethod
|
|
public void reportSubcomponentFactoryMethod(
|
|
Diagnostic.Kind diagnosticKind,
|
|
ChildFactoryMethodEdge childFactoryMethodEdge,
|
|
String messageFormat,
|
|
Object firstArg,
|
|
Object... moreArgs) {
|
|
reportSubcomponentFactoryMethod(
|
|
diagnosticKind, childFactoryMethodEdge, formatMessage(messageFormat, firstArg, moreArgs));
|
|
}
|
|
|
|
/** Adds a message to the stored aggregated message. */
|
|
private void addMessage(Diagnostic.Kind diagnosticKind, String message) {
|
|
checkNotNull(diagnosticKind);
|
|
checkNotNull(message);
|
|
checkState(currentPluginName != null);
|
|
|
|
// Add a separator if this isn't the first message
|
|
if (mergedDiagnosticKind.isPresent()) {
|
|
messageBuilder.append("\n\n");
|
|
}
|
|
|
|
mergeDiagnosticKind(diagnosticKind);
|
|
// Adds brackets as well as special color strings to make the string red and bold.
|
|
messageBuilder.append(String.format("\033[1;31m[%s]\033[0m ", currentPluginName));
|
|
messageBuilder.append(message);
|
|
}
|
|
|
|
private static String formatMessage(String messageFormat, Object firstArg, Object[] moreArgs) {
|
|
return String.format(messageFormat, asList(firstArg, moreArgs).toArray());
|
|
}
|
|
|
|
private void mergeDiagnosticKind(Diagnostic.Kind diagnosticKind) {
|
|
checkArgument(diagnosticKind != Diagnostic.Kind.MANDATORY_WARNING,
|
|
"Dagger plugins should not be issuing mandatory warnings");
|
|
if (!mergedDiagnosticKind.isPresent()) {
|
|
mergedDiagnosticKind = Optional.of(diagnosticKind);
|
|
return;
|
|
}
|
|
Diagnostic.Kind current = mergedDiagnosticKind.get();
|
|
if (current == Diagnostic.Kind.ERROR || diagnosticKind == Diagnostic.Kind.ERROR) {
|
|
mergedDiagnosticKind = Optional.of(Diagnostic.Kind.ERROR);
|
|
} else if (current == Diagnostic.Kind.WARNING || diagnosticKind == Diagnostic.Kind.WARNING) {
|
|
mergedDiagnosticKind = Optional.of(Diagnostic.Kind.WARNING);
|
|
} else if (current == Diagnostic.Kind.NOTE || diagnosticKind == Diagnostic.Kind.NOTE) {
|
|
mergedDiagnosticKind = Optional.of(Diagnostic.Kind.NOTE);
|
|
} else {
|
|
mergedDiagnosticKind = Optional.of(Diagnostic.Kind.OTHER);
|
|
}
|
|
}
|
|
}
|
|
}
|