blob: 044b1d514f4d83ae41c18a1e7049895cac629cc4 [file] [log] [blame]
// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.diagnostic.internal;
import com.android.tools.r8.diagnostic.MissingDefinitionInfo;
import com.android.tools.r8.diagnostic.MissingDefinitionsDiagnostic;
import com.android.tools.r8.errors.Unimplemented;
import com.android.tools.r8.graph.DexReference;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.position.Position;
import com.android.tools.r8.references.ClassReference;
import com.android.tools.r8.references.Reference;
import com.google.common.collect.ImmutableSortedMap;
import java.util.Collection;
import java.util.Comparator;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.SortedMap;
public class MissingDefinitionsDiagnosticImpl implements MissingDefinitionsDiagnostic {
private final boolean fatal;
private final SortedMap<ClassReference, MissingClassAccessContexts> missingClasses;
private MissingDefinitionsDiagnosticImpl(
boolean fatal, SortedMap<ClassReference, MissingClassAccessContexts> missingClasses) {
assert !missingClasses.isEmpty();
this.fatal = fatal;
this.missingClasses = missingClasses;
}
public static Builder builder() {
return new Builder();
}
@Deprecated
public Set<ClassReference> getMissingClasses() {
return missingClasses.keySet();
}
@Override
public Collection<MissingDefinitionInfo> getMissingDefinitions() {
throw new Unimplemented();
}
/** A missing class(es) failure can generally not be attributed to a single origin. */
@Override
public Origin getOrigin() {
return Origin.unknown();
}
/** A missing class(es) failure can generally not be attributed to a single position. */
@Override
public Position getPosition() {
return Position.UNKNOWN;
}
@Override
public String getDiagnosticMessage() {
return fatal ? getFatalDiagnosticMessage() : getNonFatalDiagnosticMessage();
}
private String getFatalDiagnosticMessage() {
if (missingClasses.size() == 1) {
StringBuilder builder =
new StringBuilder(
"Compilation can't be completed because the following class is missing: ");
writeMissingClass(builder, missingClasses.entrySet().iterator().next());
return builder.append(".").toString();
}
StringBuilder builder =
new StringBuilder("Compilation can't be completed because the following ")
.append(missingClasses.size())
.append(" classes are missing:");
missingClasses.forEach(
(missingClass, contexts) ->
writeMissingClass(
builder.append(System.lineSeparator()).append("- "), missingClass, contexts));
return builder.toString();
}
private String getNonFatalDiagnosticMessage() {
StringBuilder builder = new StringBuilder();
Iterator<Entry<ClassReference, MissingClassAccessContexts>> missingClassesIterator =
missingClasses.entrySet().iterator();
// The diagnostic is always non-empty.
assert missingClassesIterator.hasNext();
// Write first line.
writeMissingClass(builder.append("Missing class "), missingClassesIterator.next());
// Write remaining lines with line separator before.
missingClassesIterator.forEachRemaining(
missingClassInfo ->
writeMissingClass(
builder.append(System.lineSeparator()).append("Missing class "), missingClassInfo));
return builder.toString();
}
private static void writeMissingClass(
StringBuilder builder, Entry<ClassReference, MissingClassAccessContexts> missingClassInfo) {
writeMissingClass(builder, missingClassInfo.getKey(), missingClassInfo.getValue());
}
private static void writeMissingClass(
StringBuilder builder, ClassReference missingClass, MissingClassAccessContexts contexts) {
builder
.append(missingClass.getTypeName())
.append(contexts.getReferencedFromMessageSuffix(missingClass));
}
public static class Builder {
private boolean fatal;
private ImmutableSortedMap.Builder<ClassReference, MissingClassAccessContexts>
missingClassesBuilder =
ImmutableSortedMap.orderedBy(Comparator.comparing(ClassReference::getDescriptor));
public Builder addMissingClasses(Map<DexType, Set<DexReference>> missingClasses) {
missingClasses.forEach(
(missingClass, contexts) ->
missingClassesBuilder.put(
Reference.classFromDescriptor(missingClass.toDescriptorString()),
MissingClassAccessContexts.builder().addAll(contexts).build()));
return this;
}
public Builder setFatal(boolean fatal) {
this.fatal = fatal;
return this;
}
public MissingDefinitionsDiagnostic build() {
return new MissingDefinitionsDiagnosticImpl(fatal, missingClassesBuilder.build());
}
}
}