Include number of contexts in missing definitions diagnostic message
Bug: 179461665
Change-Id: I1851a1ebb809e27c4fc406d8daa0831185a07839
diff --git a/src/main/java/com/android/tools/r8/diagnostic/internal/MissingDefinitionContextBase.java b/src/main/java/com/android/tools/r8/diagnostic/internal/MissingDefinitionContextBase.java
index 67515a2..cf6a0d1 100644
--- a/src/main/java/com/android/tools/r8/diagnostic/internal/MissingDefinitionContextBase.java
+++ b/src/main/java/com/android/tools/r8/diagnostic/internal/MissingDefinitionContextBase.java
@@ -24,7 +24,7 @@
Origin origin;
- B setOrigin(Origin origin) {
+ public B setOrigin(Origin origin) {
this.origin = origin;
return self();
}
diff --git a/src/main/java/com/android/tools/r8/diagnostic/internal/MissingDefinitionsDiagnosticImpl.java b/src/main/java/com/android/tools/r8/diagnostic/internal/MissingDefinitionsDiagnosticImpl.java
index 97a347e..f9455a0 100644
--- a/src/main/java/com/android/tools/r8/diagnostic/internal/MissingDefinitionsDiagnosticImpl.java
+++ b/src/main/java/com/android/tools/r8/diagnostic/internal/MissingDefinitionsDiagnosticImpl.java
@@ -95,25 +95,34 @@
fieldReference -> fieldContext.setMin(fieldReference, getFieldReferenceComparator()),
methodReference -> methodContext.setMin(methodReference, getMethodReferenceComparator()));
}
-
if (fieldContext.isSet()) {
- builder
- .append(" (referenced from: ")
- .append(FieldReferenceUtils.toSourceString(fieldContext.get()))
- .append(")");
+ writeReferencedFromSuffix(
+ builder, missingDefinitionInfo, FieldReferenceUtils.toSourceString(fieldContext.get()));
} else if (methodContext.isSet()) {
- builder
- .append(" (referenced from: ")
- .append(MethodReferenceUtils.toSourceString(methodContext.get()))
- .append(")");
+ writeReferencedFromSuffix(
+ builder, missingDefinitionInfo, MethodReferenceUtils.toSourceString(methodContext.get()));
} else if (classContext.isSet()) {
- builder.append(" (referenced from: ").append(classContext.get().getTypeName()).append(")");
+ writeReferencedFromSuffix(builder, missingDefinitionInfo, classContext.get().getTypeName());
} else {
// TODO(b/175543745): Once legacy reporting is removed this should never happen.
builder.append(" (referenced from: <not known>)");
}
}
+ private static void writeReferencedFromSuffix(
+ StringBuilder builder, MissingDefinitionInfo missingDefinitionInfo, String referencedFrom) {
+ int numberOfOtherContexts = missingDefinitionInfo.getReferencedFromContexts().size() - 1;
+ assert numberOfOtherContexts >= 0;
+ builder.append(" (referenced from: ").append(referencedFrom);
+ if (numberOfOtherContexts >= 1) {
+ builder.append(", and ").append(numberOfOtherContexts).append(" other context");
+ if (numberOfOtherContexts >= 2) {
+ builder.append("s");
+ }
+ }
+ builder.append(")");
+ }
+
public static class Builder {
private ImmutableList.Builder<MissingDefinitionInfo> missingDefinitionsBuilder =
diff --git a/src/main/java/com/android/tools/r8/graph/FieldResolutionResult.java b/src/main/java/com/android/tools/r8/graph/FieldResolutionResult.java
index 8b1ca9d..dbc2119 100644
--- a/src/main/java/com/android/tools/r8/graph/FieldResolutionResult.java
+++ b/src/main/java/com/android/tools/r8/graph/FieldResolutionResult.java
@@ -25,6 +25,7 @@
return null;
}
+ @Override
public DexClassAndField getResolutionPair() {
return null;
}
diff --git a/src/main/java/com/android/tools/r8/graph/MemberResolutionResult.java b/src/main/java/com/android/tools/r8/graph/MemberResolutionResult.java
index 01294be..285859d 100644
--- a/src/main/java/com/android/tools/r8/graph/MemberResolutionResult.java
+++ b/src/main/java/com/android/tools/r8/graph/MemberResolutionResult.java
@@ -9,6 +9,10 @@
public abstract class MemberResolutionResult<
D extends DexEncodedMember<D, R>, R extends DexMember<D, R>> {
+ public DexClassAndMember<D, R> getResolutionPair() {
+ return null;
+ }
+
public abstract boolean isSuccessfulMemberResolutionResult();
public abstract SuccessfulMemberResolutionResult<D, R> asSuccessfulMemberResolutionResult();
diff --git a/src/main/java/com/android/tools/r8/graph/ResolutionResult.java b/src/main/java/com/android/tools/r8/graph/ResolutionResult.java
index 3356777..041033e 100644
--- a/src/main/java/com/android/tools/r8/graph/ResolutionResult.java
+++ b/src/main/java/com/android/tools/r8/graph/ResolutionResult.java
@@ -74,6 +74,11 @@
return null;
}
+ @Override
+ public DexClassAndMethod getResolutionPair() {
+ return null;
+ }
+
public abstract OptionalBool isAccessibleForVirtualDispatchFrom(
ProgramDefinition context, AppInfoWithClassHierarchy appInfo);
diff --git a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
index a1baa1c..ba0a71a 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -574,13 +574,13 @@
definitionFor(type, context, missingClassConsumer);
}
- private void recordMethodReference(DexMethod method, ProgramDefinition context) {
+ private void recordMethodReference(DexMethod method, ProgramDerivedContext context) {
recordMethodReference(method, context, this::reportMissingClass);
}
private void recordMethodReference(
DexMethod method,
- ProgramDefinition context,
+ ProgramDerivedContext context,
BiConsumer<DexType, ProgramDerivedContext> missingClassConsumer) {
recordTypeReference(method.holder, context, missingClassConsumer);
recordTypeReference(method.proto.returnType, context, missingClassConsumer);
@@ -1988,11 +1988,15 @@
private SingleResolutionResult resolveMethod(
DexMethod method, ProgramDefinition context, KeepReason reason, boolean interfaceInvoke) {
// Record the references in case they are not program types.
- recordMethodReference(method, context);
ResolutionResult resolutionResult = appInfo.resolveMethod(method, interfaceInvoke);
- if (resolutionResult.isFailedResolution()) {
+ if (resolutionResult.isSingleResolution()) {
+ recordMethodReference(
+ method, resolutionResult.getResolutionPair().asProgramDerivedContext(context));
+ } else {
+ assert resolutionResult.isFailedResolution();
markFailedMethodResolutionTargets(
method, resolutionResult.asFailedResolution(), context, reason);
+ recordMethodReference(method, context);
}
return resolutionResult.asSingleResolution();
}
@@ -2252,19 +2256,23 @@
// TODO(b/157107464): See if we can clean this up.
|| (initialPrunedTypes != null && initialPrunedTypes.contains(clazz))
: "Unexpected missing class `" + clazz.toSourceString() + "`";
- missingClassesBuilder.addNewMissingClass(clazz, context);
- }
-
- @Deprecated
- private void reportMissingClassWithoutContext(DexType clazz) {
- assert !mode.isFinalTreeShaking()
- || missingClassesBuilder.wasAlreadyMissing(clazz)
- || appView.dexItemFactory().isPossiblyCompilerSynthesizedType(clazz)
- || initialDeadProtoTypes.contains(clazz)
- // TODO(b/157107464): See if we can clean this up.
- || (initialPrunedTypes != null && initialPrunedTypes.contains(clazz))
- : "Unexpected missing class `" + clazz.toSourceString() + "`";
- missingClassesBuilder.legacyAddNewMissingClass(clazz);
+ // Do not report missing classes from D8/R8 synthesized methods on non-synthetic classes (for
+ // example, lambda accessibility bridges).
+ // TODO(b/180376674): Clean this up. Ideally the D8/R8 synthesized methods would be synthesized
+ // using synthetic items, such that the synthetic items infrastructure would track the
+ // synthesizing contexts for these methods as well. That way, this would just work without any
+ // special handling because the mapping to the synthesizing contexts would also work for these
+ // synthetic methods.
+ if (context.isProgramContext()
+ && context.getContext().isMethod()
+ && context.getContext().asMethod().getDefinition().isD8R8Synthesized()
+ && !appView
+ .getSyntheticItems()
+ .isSyntheticClass(context.getContext().asProgramDefinition().getContextClass())) {
+ missingClassesBuilder.ignoreNewMissingClass(clazz);
+ } else {
+ missingClassesBuilder.addNewMissingClass(clazz, context);
+ }
}
/**
diff --git a/src/main/java/com/android/tools/r8/shaking/MissingClasses.java b/src/main/java/com/android/tools/r8/shaking/MissingClasses.java
index bde1d07..bdca121 100644
--- a/src/main/java/com/android/tools/r8/shaking/MissingClasses.java
+++ b/src/main/java/com/android/tools/r8/shaking/MissingClasses.java
@@ -113,7 +113,9 @@
}
public boolean contains(DexType type) {
- return alreadyMissingClasses.contains(type) || newMissingClasses.containsKey(type);
+ return alreadyMissingClasses.contains(type)
+ || newMissingClasses.containsKey(type)
+ || newIgnoredMissingClasses.contains(type);
}
Builder removeAlreadyMissingClasses(Iterable<DexType> types) {
diff --git a/src/main/java/com/android/tools/r8/utils/Box.java b/src/main/java/com/android/tools/r8/utils/Box.java
index 615b321..de67cf6 100644
--- a/src/main/java/com/android/tools/r8/utils/Box.java
+++ b/src/main/java/com/android/tools/r8/utils/Box.java
@@ -5,6 +5,7 @@
package com.android.tools.r8.utils;
import java.util.Comparator;
+import java.util.Objects;
import java.util.function.Supplier;
public class Box<T> {
@@ -47,4 +48,18 @@
value = newValue;
return oldValue;
}
+
+ @Override
+ public boolean equals(Object object) {
+ if (object == null || getClass() != object.getClass()) {
+ return false;
+ }
+ Box<?> box = (Box<?>) object;
+ return Objects.equals(value, box.value);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(value);
+ }
}
diff --git a/src/test/java/com/android/tools/r8/TestBase.java b/src/test/java/com/android/tools/r8/TestBase.java
index 8648de9..276f921 100644
--- a/src/test/java/com/android/tools/r8/TestBase.java
+++ b/src/test/java/com/android/tools/r8/TestBase.java
@@ -39,6 +39,7 @@
import com.android.tools.r8.graph.SubtypingInfo;
import com.android.tools.r8.jasmin.JasminBuilder;
import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.origin.PathOrigin;
import com.android.tools.r8.references.ClassReference;
import com.android.tools.r8.references.FieldReference;
import com.android.tools.r8.references.MethodReference;
@@ -1691,6 +1692,10 @@
return DescriptorUtils.javaTypeToDescriptor(typeName(clazz));
}
+ public static PathOrigin getOrigin(Class<?> clazz) {
+ return new PathOrigin(ToolHelper.getClassFileForTestClass(clazz));
+ }
+
public static String typeName(Class<?> clazz) {
return clazz.getTypeName();
}
diff --git a/src/test/java/com/android/tools/r8/ThrowableConsumer.java b/src/test/java/com/android/tools/r8/ThrowableConsumer.java
index f19039b..5720c7d 100644
--- a/src/test/java/com/android/tools/r8/ThrowableConsumer.java
+++ b/src/test/java/com/android/tools/r8/ThrowableConsumer.java
@@ -27,4 +27,8 @@
consumer.accept(formal);
};
}
+
+ static <Formal> ThrowableConsumer<Formal> empty() {
+ return ignore -> {};
+ }
}
diff --git a/src/test/java/com/android/tools/r8/diagnosticinspector/FoundMissingDefinitionContextSubject.java b/src/test/java/com/android/tools/r8/diagnosticinspector/FoundMissingDefinitionContextSubject.java
new file mode 100644
index 0000000..1138239
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/diagnosticinspector/FoundMissingDefinitionContextSubject.java
@@ -0,0 +1,39 @@
+// 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.diagnosticinspector;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.diagnostic.MissingDefinitionContext;
+import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.references.FieldReference;
+import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.utils.Box;
+
+public class FoundMissingDefinitionContextSubject {
+
+ private final MissingDefinitionContext context;
+
+ public FoundMissingDefinitionContextSubject(MissingDefinitionContext context) {
+ this.context = context;
+ }
+
+ public FoundMissingDefinitionContextSubject assertEqualTo(
+ MissingDefinitionContext expectedContext) {
+ Box<ClassReference> classReference = new Box<>();
+ Box<FieldReference> fieldReference = new Box<>();
+ Box<MethodReference> methodReference = new Box<>();
+ context.getReference(classReference::set, fieldReference::set, methodReference::set);
+ Box<ClassReference> expectedClassReference = new Box<>();
+ Box<FieldReference> expectedFieldReference = new Box<>();
+ Box<MethodReference> expectedMethodReference = new Box<>();
+ expectedContext.getReference(
+ expectedClassReference::set, expectedFieldReference::set, expectedMethodReference::set);
+ assertEquals(classReference, expectedClassReference);
+ assertEquals(fieldReference, expectedFieldReference);
+ assertEquals(methodReference, expectedMethodReference);
+ return this;
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/diagnosticinspector/FoundMissingDefinitionInfoSubject.java b/src/test/java/com/android/tools/r8/diagnosticinspector/FoundMissingDefinitionInfoSubject.java
new file mode 100644
index 0000000..f51752f
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/diagnosticinspector/FoundMissingDefinitionInfoSubject.java
@@ -0,0 +1,71 @@
+// 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.diagnosticinspector;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import com.android.tools.r8.diagnostic.MissingDefinitionContext;
+import com.android.tools.r8.diagnostic.MissingDefinitionInfo;
+import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.references.FieldReference;
+import com.android.tools.r8.references.MethodReference;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class FoundMissingDefinitionInfoSubject {
+
+ private final MissingDefinitionInfo missingDefinitionInfo;
+
+ private final Map<ClassReference, FoundMissingDefinitionContextSubject> classContexts =
+ new HashMap<>();
+ private final Map<FieldReference, FoundMissingDefinitionContextSubject> fieldContexts =
+ new HashMap<>();
+ private final Map<MethodReference, FoundMissingDefinitionContextSubject> methodContexts =
+ new HashMap<>();
+
+ public FoundMissingDefinitionInfoSubject(MissingDefinitionInfo missingDefinitionInfo) {
+ this.missingDefinitionInfo = missingDefinitionInfo;
+ missingDefinitionInfo
+ .getReferencedFromContexts()
+ .forEach(
+ context ->
+ context.getReference(
+ classContext ->
+ classContexts.put(
+ classContext, new FoundMissingDefinitionContextSubject(context)),
+ fieldContext ->
+ fieldContexts.put(
+ fieldContext, new FoundMissingDefinitionContextSubject(context)),
+ methodContext ->
+ methodContexts.put(
+ methodContext, new FoundMissingDefinitionContextSubject(context))));
+ }
+
+ public FoundMissingDefinitionInfoSubject assertExactContexts(
+ List<MissingDefinitionContext> expectedContexts) {
+ assertEquals(expectedContexts.size(), missingDefinitionInfo.getReferencedFromContexts().size());
+ expectedContexts.forEach(
+ expectedContext ->
+ expectedContext.getReference(
+ classContext -> {
+ FoundMissingDefinitionContextSubject subject = classContexts.get(classContext);
+ assertNotNull(subject);
+ subject.assertEqualTo(expectedContext);
+ },
+ fieldContext -> {
+ FoundMissingDefinitionContextSubject subject = fieldContexts.get(fieldContext);
+ assertNotNull(subject);
+ subject.assertEqualTo(expectedContext);
+ },
+ methodContext -> {
+ FoundMissingDefinitionContextSubject subject = methodContexts.get(methodContext);
+ assertNotNull(subject);
+ subject.assertEqualTo(expectedContext);
+ }));
+ return this;
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/diagnosticinspector/FoundMissingDefinitionsDiagnosticSubject.java b/src/test/java/com/android/tools/r8/diagnosticinspector/FoundMissingDefinitionsDiagnosticSubject.java
index c7aea09..ac63c98 100644
--- a/src/test/java/com/android/tools/r8/diagnosticinspector/FoundMissingDefinitionsDiagnosticSubject.java
+++ b/src/test/java/com/android/tools/r8/diagnosticinspector/FoundMissingDefinitionsDiagnosticSubject.java
@@ -4,16 +4,37 @@
package com.android.tools.r8.diagnosticinspector;
+import static com.android.tools.r8.utils.ConsumerUtils.emptyConsumer;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import com.android.tools.r8.ThrowableConsumer;
+import com.android.tools.r8.diagnostic.MissingDefinitionContext;
+import com.android.tools.r8.diagnostic.MissingDefinitionInfo;
import com.android.tools.r8.diagnostic.MissingDefinitionsDiagnostic;
import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.references.Reference;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
public class FoundMissingDefinitionsDiagnosticSubject
extends FoundDiagnosticSubject<MissingDefinitionsDiagnostic> {
+ private final Map<ClassReference, MissingDefinitionInfo> missingClasses = new HashMap<>();
+
public FoundMissingDefinitionsDiagnosticSubject(MissingDefinitionsDiagnostic diagnostic) {
super(diagnostic);
+ diagnostic
+ .getMissingDefinitions()
+ .forEach(
+ missingDefinitionInfo ->
+ missingDefinitionInfo.getMissingDefinition(
+ classReference -> missingClasses.put(classReference, missingDefinitionInfo),
+ emptyConsumer(),
+ emptyConsumer()));
}
public FoundMissingDefinitionsDiagnosticSubject assertHasMessage(String expectedMessage) {
@@ -22,16 +43,56 @@
}
public FoundMissingDefinitionsDiagnosticSubject assertIsMissingClass(Class<?> clazz) {
- return this;
+ return assertIsMissingClass(Reference.classFromClass(clazz));
}
public FoundMissingDefinitionsDiagnosticSubject assertIsMissingClass(
ClassReference classReference) {
+ assertTrue(missingClasses.containsKey(classReference));
return this;
}
+ public FoundMissingDefinitionsDiagnosticSubject assertIsMissingClassWithExactContexts(
+ ClassReference classReference, MissingDefinitionContext... expectedContexts) {
+ return assertIsMissingClassWithExactContexts(classReference, Arrays.asList(expectedContexts));
+ }
+
+ public FoundMissingDefinitionsDiagnosticSubject assertIsMissingClassWithExactContexts(
+ ClassReference classReference, List<MissingDefinitionContext> expectedContexts) {
+ return inspectMissingClassInfo(
+ classReference,
+ missingClassInfoSubject -> missingClassInfoSubject.assertExactContexts(expectedContexts));
+ }
+
public FoundMissingDefinitionsDiagnosticSubject assertNumberOfMissingClasses(int expected) {
assertEquals(expected, getDiagnostic().getMissingDefinitions().size());
return this;
}
+
+ public FoundMissingDefinitionsDiagnosticSubject applyIf(
+ boolean condition, ThrowableConsumer<FoundMissingDefinitionsDiagnosticSubject> thenConsumer) {
+ return applyIf(condition, thenConsumer, ThrowableConsumer.empty());
+ }
+
+ public FoundMissingDefinitionsDiagnosticSubject applyIf(
+ boolean condition,
+ ThrowableConsumer<FoundMissingDefinitionsDiagnosticSubject> thenConsumer,
+ ThrowableConsumer<FoundMissingDefinitionsDiagnosticSubject> elseConsumer) {
+ if (condition) {
+ thenConsumer.acceptWithRuntimeException(this);
+ } else {
+ elseConsumer.acceptWithRuntimeException(this);
+ }
+ return this;
+ }
+
+ public FoundMissingDefinitionsDiagnosticSubject inspectMissingClassInfo(
+ ClassReference classReference,
+ ThrowableConsumer<FoundMissingDefinitionInfoSubject> inspector) {
+ MissingDefinitionInfo missingDefinitionInfo = missingClasses.get(classReference);
+ assertNotNull(missingDefinitionInfo);
+ inspector.acceptWithRuntimeException(
+ new FoundMissingDefinitionInfoSubject(missingDefinitionInfo));
+ return this;
+ }
}
diff --git a/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromUnusedLambdaParameterTest.java b/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromUnusedLambdaParameterTest.java
index a09c4d0..334b107 100644
--- a/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromUnusedLambdaParameterTest.java
+++ b/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromUnusedLambdaParameterTest.java
@@ -11,19 +11,35 @@
import com.android.tools.r8.TestDiagnosticMessages;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.ThrowableConsumer;
-import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.diagnostic.MissingDefinitionContext;
+import com.android.tools.r8.diagnostic.internal.MissingDefinitionMethodContext;
import com.android.tools.r8.references.Reference;
import com.google.common.collect.ImmutableList;
import org.junit.Test;
public class MissingClassReferencedFromUnusedLambdaParameterTest extends MissingClassesTestBase {
- private static final MethodReference referencedFrom =
- Reference.method(
- Reference.classFromClass(Main.class),
- "lambda$main$0",
- ImmutableList.of(Reference.classFromClass(MissingClass.class)),
- null);
+ private static final MissingDefinitionContext[] referencedFrom =
+ new MissingDefinitionContext[] {
+ MissingDefinitionMethodContext.builder()
+ .setMethodContext(
+ Reference.method(
+ Reference.classFromClass(Main.class),
+ "lambda$main$0",
+ ImmutableList.of(Reference.classFromClass(MissingClass.class)),
+ null))
+ .setOrigin(getOrigin(Main.class))
+ .build(),
+ MissingDefinitionMethodContext.builder()
+ .setMethodContext(
+ Reference.method(
+ Reference.classFromClass(Main.class),
+ "main",
+ ImmutableList.of(Reference.array(Reference.classFromClass(String.class), 1)),
+ null))
+ .setOrigin(getOrigin(Main.class))
+ .build(),
+ };
public MissingClassReferencedFromUnusedLambdaParameterTest(TestParameters parameters) {
super(parameters);
diff --git a/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromUnusedLambdaReturnTest.java b/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromUnusedLambdaReturnTest.java
index c196013..bd122d9 100644
--- a/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromUnusedLambdaReturnTest.java
+++ b/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromUnusedLambdaReturnTest.java
@@ -11,19 +11,36 @@
import com.android.tools.r8.TestDiagnosticMessages;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.ThrowableConsumer;
-import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.diagnostic.MissingDefinitionContext;
+import com.android.tools.r8.diagnostic.internal.MissingDefinitionMethodContext;
import com.android.tools.r8.references.Reference;
+import com.google.common.collect.ImmutableList;
import java.util.Collections;
import org.junit.Test;
public class MissingClassReferencedFromUnusedLambdaReturnTest extends MissingClassesTestBase {
- private static final MethodReference referencedFrom =
- Reference.method(
- Reference.classFromClass(Main.class),
- "lambda$main$0",
- Collections.emptyList(),
- Reference.classFromClass(MissingClass.class));
+ private static final MissingDefinitionContext[] referencedFrom =
+ new MissingDefinitionContext[] {
+ MissingDefinitionMethodContext.builder()
+ .setMethodContext(
+ Reference.method(
+ Reference.classFromClass(Main.class),
+ "lambda$main$0",
+ Collections.emptyList(),
+ Reference.classFromClass(MissingClass.class)))
+ .setOrigin(getOrigin(Main.class))
+ .build(),
+ MissingDefinitionMethodContext.builder()
+ .setMethodContext(
+ Reference.method(
+ Reference.classFromClass(Main.class),
+ "main",
+ ImmutableList.of(Reference.array(Reference.classFromClass(String.class), 1)),
+ null))
+ .setOrigin(getOrigin(Main.class))
+ .build(),
+ };
public MissingClassReferencedFromUnusedLambdaReturnTest(TestParameters parameters) {
super(parameters);
diff --git a/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromUsedLambdaParameterTest.java b/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromUsedLambdaParameterTest.java
index 2da56a8..271ab9a 100644
--- a/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromUsedLambdaParameterTest.java
+++ b/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromUsedLambdaParameterTest.java
@@ -9,21 +9,44 @@
import com.android.tools.r8.TestDiagnosticMessages;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.ThrowableConsumer;
-import com.android.tools.r8.missingclasses.MissingClassReferencedFromUsedLambdaReturnTest.I;
-import com.android.tools.r8.missingclasses.MissingClassReferencedFromUsedLambdaReturnTest.Main;
-import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.diagnostic.MissingDefinitionContext;
+import com.android.tools.r8.diagnostic.internal.MissingDefinitionMethodContext;
import com.android.tools.r8.references.Reference;
import com.google.common.collect.ImmutableList;
import org.junit.Test;
public class MissingClassReferencedFromUsedLambdaParameterTest extends MissingClassesTestBase {
- private static final MethodReference referencedFrom =
- Reference.method(
- Reference.classFromClass(I.class),
- "m",
- ImmutableList.of(Reference.classFromClass(MissingClass.class)),
- null);
+ private final MissingDefinitionContext[] referencedFrom =
+ new MissingDefinitionContext[] {
+ MissingDefinitionMethodContext.builder()
+ .setMethodContext(
+ Reference.method(
+ Reference.classFromClass(I.class),
+ "m",
+ ImmutableList.of(Reference.classFromClass(MissingClass.class)),
+ null))
+ .setOrigin(getOrigin(I.class))
+ .build(),
+ MissingDefinitionMethodContext.builder()
+ .setMethodContext(
+ Reference.method(
+ Reference.classFromClass(Main.class),
+ "lambda$main$0",
+ ImmutableList.of(Reference.classFromClass(MissingClass.class)),
+ null))
+ .setOrigin(getOrigin(Main.class))
+ .build(),
+ MissingDefinitionMethodContext.builder()
+ .setMethodContext(
+ Reference.method(
+ Reference.classFromClass(Main.class),
+ "main",
+ ImmutableList.of(Reference.array(Reference.classFromClass(String.class), 1)),
+ null))
+ .setOrigin(getOrigin(Main.class))
+ .build()
+ };
public MissingClassReferencedFromUsedLambdaParameterTest(TestParameters parameters) {
super(parameters);
diff --git a/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromUsedLambdaReturnTest.java b/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromUsedLambdaReturnTest.java
index 1b6781d..704f0d5 100644
--- a/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromUsedLambdaReturnTest.java
+++ b/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromUsedLambdaReturnTest.java
@@ -9,19 +9,45 @@
import com.android.tools.r8.TestDiagnosticMessages;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.ThrowableConsumer;
-import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.diagnostic.MissingDefinitionContext;
+import com.android.tools.r8.diagnostic.internal.MissingDefinitionMethodContext;
import com.android.tools.r8.references.Reference;
+import com.google.common.collect.ImmutableList;
import java.util.Collections;
import org.junit.Test;
public class MissingClassReferencedFromUsedLambdaReturnTest extends MissingClassesTestBase {
- private static final MethodReference referencedFrom =
- Reference.method(
- Reference.classFromClass(I.class),
- "m",
- Collections.emptyList(),
- Reference.classFromClass(MissingClass.class));
+ private final MissingDefinitionContext[] referencedFrom =
+ new MissingDefinitionContext[] {
+ MissingDefinitionMethodContext.builder()
+ .setMethodContext(
+ Reference.method(
+ Reference.classFromClass(I.class),
+ "m",
+ Collections.emptyList(),
+ Reference.classFromClass(MissingClass.class)))
+ .setOrigin(getOrigin(I.class))
+ .build(),
+ MissingDefinitionMethodContext.builder()
+ .setMethodContext(
+ Reference.method(
+ Reference.classFromClass(Main.class),
+ "lambda$main$0",
+ Collections.emptyList(),
+ Reference.classFromClass(MissingClass.class)))
+ .setOrigin(getOrigin(Main.class))
+ .build(),
+ MissingDefinitionMethodContext.builder()
+ .setMethodContext(
+ Reference.method(
+ Reference.classFromClass(Main.class),
+ "main",
+ ImmutableList.of(Reference.array(Reference.classFromClass(String.class), 1)),
+ null))
+ .setOrigin(getOrigin(Main.class))
+ .build()
+ };
public MissingClassReferencedFromUsedLambdaReturnTest(TestParameters parameters) {
super(parameters);
diff --git a/src/test/java/com/android/tools/r8/missingclasses/MissingClassesTestBase.java b/src/test/java/com/android/tools/r8/missingclasses/MissingClassesTestBase.java
index 869f0f5..2a9833d 100644
--- a/src/test/java/com/android/tools/r8/missingclasses/MissingClassesTestBase.java
+++ b/src/test/java/com/android/tools/r8/missingclasses/MissingClassesTestBase.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.missingclasses;
+import static org.junit.Assert.assertTrue;
import com.android.tools.r8.CompilationFailedException;
import com.android.tools.r8.R8FullTestBuilder;
@@ -12,10 +13,12 @@
import com.android.tools.r8.TestDiagnosticMessages;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.ThrowableConsumer;
+import com.android.tools.r8.diagnostic.MissingDefinitionContext;
import com.android.tools.r8.references.ClassReference;
import com.android.tools.r8.references.FieldReference;
import com.android.tools.r8.references.MethodReference;
import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.utils.Box;
import com.android.tools.r8.utils.FieldReferenceUtils;
import com.android.tools.r8.utils.InternalOptions.TestingOptions;
import com.android.tools.r8.utils.MethodReferenceUtils;
@@ -107,13 +110,16 @@
void inspectDiagnosticsWithIgnoreWarnings(
TestDiagnosticMessages diagnostics, ClassReference referencedFrom) {
inspectDiagnosticsWithIgnoreWarnings(
- diagnostics, getExpectedDiagnosticMessage(referencedFrom, ClassReference::getTypeName));
+ diagnostics,
+ null,
+ getExpectedDiagnosticMessage(referencedFrom, ClassReference::getTypeName));
}
void inspectDiagnosticsWithIgnoreWarnings(
TestDiagnosticMessages diagnostics, FieldReference referencedFrom) {
inspectDiagnosticsWithIgnoreWarnings(
diagnostics,
+ null,
getExpectedDiagnosticMessage(referencedFrom, FieldReferenceUtils::toSourceString));
}
@@ -121,31 +127,59 @@
TestDiagnosticMessages diagnostics, MethodReference referencedFrom) {
inspectDiagnosticsWithIgnoreWarnings(
diagnostics,
+ null,
getExpectedDiagnosticMessage(referencedFrom, MethodReferenceUtils::toSourceString));
}
void inspectDiagnosticsWithIgnoreWarnings(
- TestDiagnosticMessages diagnostics, String expectedDiagnosticMessage) {
+ TestDiagnosticMessages diagnostics, MissingDefinitionContext[] referencedFrom) {
+ assertTrue(referencedFrom.length > 0);
+ Box<String> referencedFromSourceString = new Box<>();
+ referencedFrom[0].getReference(
+ classReference -> referencedFromSourceString.set(classReference.getTypeName()),
+ fieldReference ->
+ referencedFromSourceString.set(FieldReferenceUtils.toSourceString(fieldReference)),
+ methodReference ->
+ referencedFromSourceString.set(MethodReferenceUtils.toSourceString(methodReference)));
+ inspectDiagnosticsWithIgnoreWarnings(
+ diagnostics,
+ referencedFrom,
+ getExpectedDiagnosticMessage(referencedFromSourceString.get(), referencedFrom.length));
+ }
+
+ void inspectDiagnosticsWithIgnoreWarnings(
+ TestDiagnosticMessages diagnostics,
+ MissingDefinitionContext[] referencedFrom,
+ String expectedDiagnosticMessage) {
diagnostics
.assertOnlyWarnings()
.inspectWarnings(
diagnostic ->
diagnostic
.assertIsMissingDefinitionsDiagnostic()
- .assertIsMissingClass(getMissingClassReference())
- .assertHasMessage(expectedDiagnosticMessage));
+ .applyIf(
+ referencedFrom != null,
+ checker ->
+ checker.assertIsMissingClassWithExactContexts(
+ getMissingClassReference(), referencedFrom),
+ checker -> checker.assertIsMissingClass(getMissingClassReference()))
+ .assertHasMessage(expectedDiagnosticMessage)
+ .assertNumberOfMissingClasses(1));
}
void inspectDiagnosticsWithNoRules(
TestDiagnosticMessages diagnostics, ClassReference referencedFrom) {
inspectDiagnosticsWithNoRules(
- diagnostics, getExpectedDiagnosticMessage(referencedFrom, ClassReference::getTypeName));
+ diagnostics,
+ null,
+ getExpectedDiagnosticMessage(referencedFrom, ClassReference::getTypeName));
}
void inspectDiagnosticsWithNoRules(
TestDiagnosticMessages diagnostics, FieldReference referencedFrom) {
inspectDiagnosticsWithNoRules(
diagnostics,
+ null,
getExpectedDiagnosticMessage(referencedFrom, FieldReferenceUtils::toSourceString));
}
@@ -153,27 +187,63 @@
TestDiagnosticMessages diagnostics, MethodReference referencedFrom) {
inspectDiagnosticsWithNoRules(
diagnostics,
+ null,
getExpectedDiagnosticMessage(referencedFrom, MethodReferenceUtils::toSourceString));
}
void inspectDiagnosticsWithNoRules(
- TestDiagnosticMessages diagnostics, String expectedDiagnosticMessage) {
+ TestDiagnosticMessages diagnostics, MissingDefinitionContext[] referencedFrom) {
+ assertTrue(referencedFrom.length > 0);
+ Box<String> referencedFromSourceString = new Box<>();
+ referencedFrom[0].getReference(
+ classReference -> referencedFromSourceString.set(classReference.getTypeName()),
+ fieldReference ->
+ referencedFromSourceString.set(FieldReferenceUtils.toSourceString(fieldReference)),
+ methodReference ->
+ referencedFromSourceString.set(MethodReferenceUtils.toSourceString(methodReference)));
+ inspectDiagnosticsWithNoRules(
+ diagnostics,
+ referencedFrom,
+ getExpectedDiagnosticMessage(referencedFromSourceString.get(), referencedFrom.length));
+ }
+
+ void inspectDiagnosticsWithNoRules(
+ TestDiagnosticMessages diagnostics,
+ MissingDefinitionContext[] referencedFrom,
+ String expectedDiagnosticMessage) {
diagnostics
.assertOnlyErrors()
.inspectErrors(
diagnostic ->
diagnostic
.assertIsMissingDefinitionsDiagnostic()
+ .applyIf(
+ referencedFrom != null,
+ checker ->
+ checker.assertIsMissingClassWithExactContexts(
+ getMissingClassReference(), referencedFrom),
+ checker -> checker.assertIsMissingClass(getMissingClassReference()))
.assertHasMessage(expectedDiagnosticMessage)
- .assertIsMissingClass(getMissingClassReference()));
+ .assertNumberOfMissingClasses(1));
}
private <T> String getExpectedDiagnosticMessage(
T referencedFrom, Function<T, String> toSourceStringFunction) {
- return "Missing class "
- + getMissingClassReference().getTypeName()
- + " (referenced from: "
- + toSourceStringFunction.apply(referencedFrom)
- + ")";
+ return getExpectedDiagnosticMessage(toSourceStringFunction.apply(referencedFrom), 1);
+ }
+
+ private <T> String getExpectedDiagnosticMessage(String referencedFrom, int numberOfContexts) {
+ StringBuilder builder =
+ new StringBuilder("Missing class ")
+ .append(getMissingClassReference().getTypeName())
+ .append(" (referenced from: ")
+ .append(referencedFrom);
+ if (numberOfContexts > 1) {
+ builder.append(", and ").append(numberOfContexts - 1).append(" other context");
+ if (numberOfContexts > 2) {
+ builder.append("s");
+ }
+ }
+ return builder.append(")").toString();
}
}