Version 1.5.40

Cherry pick: Add desugar-specific diagnostics for missing types.
CL: https://r8-review.googlesource.com/c/r8/+/38363
Bug: 118842646

Change-Id: I96fbea63c86d1c3ac3b147f88e2658d351b2c107
diff --git a/src/main/java/com/android/tools/r8/Version.java b/src/main/java/com/android/tools/r8/Version.java
index 7d84af5..7c7249b 100644
--- a/src/main/java/com/android/tools/r8/Version.java
+++ b/src/main/java/com/android/tools/r8/Version.java
@@ -11,7 +11,7 @@
 
   // This field is accessed from release scripts using simple pattern matching.
   // Therefore, changing this field could break our release scripts.
-  public static final String LABEL = "1.5.39";
+  public static final String LABEL = "1.5.40";
 
   private Version() {
   }
diff --git a/src/main/java/com/android/tools/r8/errors/DesugarDiagnostic.java b/src/main/java/com/android/tools/r8/errors/DesugarDiagnostic.java
new file mode 100644
index 0000000..59711e9
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/errors/DesugarDiagnostic.java
@@ -0,0 +1,11 @@
+// Copyright (c) 2019, 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.errors;
+
+import com.android.tools.r8.Diagnostic;
+import com.android.tools.r8.KeepForSubclassing;
+
+/** Common interface type for all diagnostics related to desugaring. */
+@KeepForSubclassing
+public interface DesugarDiagnostic extends Diagnostic {}
diff --git a/src/main/java/com/android/tools/r8/errors/InterfaceDesugarDiagnostic.java b/src/main/java/com/android/tools/r8/errors/InterfaceDesugarDiagnostic.java
new file mode 100644
index 0000000..7bd18fd
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/errors/InterfaceDesugarDiagnostic.java
@@ -0,0 +1,12 @@
+// Copyright (c) 2019, 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.errors;
+
+import com.android.tools.r8.KeepForSubclassing;
+
+/** Common interface type for all diagnostics related to interface-method desugaring. */
+@KeepForSubclassing
+public interface InterfaceDesugarDiagnostic extends DesugarDiagnostic {
+
+}
diff --git a/src/main/java/com/android/tools/r8/errors/InterfaceDesugarMissingTypeDiagnostic.java b/src/main/java/com/android/tools/r8/errors/InterfaceDesugarMissingTypeDiagnostic.java
new file mode 100644
index 0000000..b3f3fd6
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/errors/InterfaceDesugarMissingTypeDiagnostic.java
@@ -0,0 +1,88 @@
+// Copyright (c) 2019, 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.errors;
+
+import com.android.tools.r8.Keep;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.position.Position;
+import com.android.tools.r8.references.ClassReference;
+
+/**
+ * Diagnostic for missing types needed for correct desugaring of default/static interface methods.
+ */
+@Keep
+public class InterfaceDesugarMissingTypeDiagnostic implements DesugarDiagnostic {
+
+  private final Origin origin;
+  private final Position position;
+  private final ClassReference missingType;
+  private final ClassReference contextType;
+
+  // Note: the implementing context is not yet made part of the public API as the context could be
+  // both in the implements clause or from a lambda class in a member.
+  private final ClassReference implementingContextType;
+
+  public InterfaceDesugarMissingTypeDiagnostic(
+      Origin origin,
+      Position position,
+      ClassReference missingType,
+      ClassReference contextType,
+      ClassReference implementingContextType) {
+    assert origin != null;
+    assert position != null;
+    assert missingType != null;
+    assert contextType != null;
+    this.origin = origin;
+    this.position = position;
+    this.missingType = missingType;
+    this.contextType = contextType;
+    // The implementing context is optional.
+    this.implementingContextType = implementingContextType;
+  }
+
+  /** Get the origin of a class leading to this warning. */
+  @Override
+  public Origin getOrigin() {
+    return origin;
+  }
+
+  /** Get additional position information about the context leading to this warning. */
+  @Override
+  public Position getPosition() {
+    return position;
+  }
+
+  /** Get the type that is missing. */
+  public ClassReference getMissingType() {
+    return missingType;
+  }
+
+  /** Get the type that requires knowledge of the missing type. */
+  public ClassReference getContextType() {
+    return contextType;
+  }
+
+  @Override
+  public String getDiagnosticMessage() {
+    StringBuilder builder =
+        new StringBuilder()
+            .append("Type `")
+            .append(missingType.getTypeName())
+            .append("` was not found, ")
+            .append("it is required for default or static interface methods desugaring of `");
+    if (position != Position.UNKNOWN) {
+      builder.append(position.getDescription());
+    } else {
+      builder.append(contextType.getTypeName());
+    }
+    builder.append("`");
+    if (implementingContextType != null) {
+      builder
+          .append(" This missing interface is declared in the direct hierarchy of `")
+          .append(implementingContextType)
+          .append("`");
+    }
+    return builder.toString();
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
index b949b72..3bec073 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
@@ -12,7 +12,6 @@
 import com.android.tools.r8.graph.DexCallSite;
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexEncodedMethod;
-import com.android.tools.r8.graph.DexItem;
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexLibraryClass;
 import com.android.tools.r8.graph.DexMethod;
@@ -32,8 +31,8 @@
 import com.android.tools.r8.ir.conversion.IRConverter;
 import com.android.tools.r8.ir.desugar.DefaultMethodsHelper.Collection;
 import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.position.MethodPosition;
 import com.android.tools.r8.utils.InternalOptions;
-import com.android.tools.r8.utils.StringDiagnostic;
 import com.google.common.collect.Sets;
 import java.util.ListIterator;
 import java.util.Map;
@@ -95,11 +94,6 @@
       new ConcurrentHashMap<>();
 
   /**
-   * A set of dexitems we have reported missing to dedupe warnings.
-   */
-  private final Set<DexItem> reportedMissing = Sets.newConcurrentHashSet();
-
-  /**
    * Defines a minor variation in desugaring.
    */
   public enum Flavor {
@@ -490,44 +484,14 @@
 
   public void warnMissingInterface(
       DexClass classToDesugar, DexClass implementing, DexType missing) {
-    // TODO think about using a common deduplicating mechanic with Enqueuer
-    if (!reportedMissing.add(missing)) {
-      return;
-    }
-    StringBuilder builder = new StringBuilder();
-    builder
-        .append("Interface `")
-        .append(missing.toSourceString())
-        .append("` not found. It's needed to make sure desugaring of `")
-        .append(classToDesugar.toSourceString())
-        .append("` is correct. Desugaring will assume that this interface has no default method.");
-    if (classToDesugar != implementing) {
-      builder
-          .append(" This missing interface is declared in the direct hierarchy of `")
-          .append(implementing)
-          .append("`");
-    }
-    options.reporter.warning(
-        new StringDiagnostic(builder.toString(), classToDesugar.getOrigin()));
+    options.warningMissingInterfaceForDesugar(classToDesugar, implementing, missing);
   }
 
   private void warnMissingType(DexMethod referencedFrom, DexType missing) {
-    // TODO think about using a common deduplicating mechanic with Enqueuer
-    if (!reportedMissing.add(missing)) {
-      return;
-    }
-    DexMethod originalReferencedFrom =
-        appView.graphLense().getOriginalMethodSignature(referencedFrom);
-    StringBuilder builder = new StringBuilder();
-    builder
-        .append("Type `")
-        .append(missing.toSourceString())
-        .append("` was not found, ")
-        .append("it is required for default or static interface methods desugaring of `")
-        .append(originalReferencedFrom.toSourceString())
-        .append("`");
-    options.reporter.warning(
-        new StringDiagnostic(builder.toString(), getMethodOrigin(originalReferencedFrom)));
+    DexMethod method = appView.graphLense().getOriginalMethodSignature(referencedFrom);
+    Origin origin = getMethodOrigin(method);
+    MethodPosition position = new MethodPosition(method);
+    options.warningMissingTypeForDesugar(origin, position, missing, method.holder);
   }
 
   private Origin getMethodOrigin(DexMethod method) {
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
index 8ff922b..1765a64 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -13,9 +13,12 @@
 import com.android.tools.r8.Version;
 import com.android.tools.r8.dex.Marker;
 import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.errors.InterfaceDesugarMissingTypeDiagnostic;
 import com.android.tools.r8.errors.InvalidDebugInfoException;
 import com.android.tools.r8.experimental.graphinfo.GraphConsumer;
+import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexItem;
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexType;
@@ -23,6 +26,8 @@
 import com.android.tools.r8.ir.optimize.Inliner;
 import com.android.tools.r8.naming.InterfaceMethodNameMinifier;
 import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.position.Position;
+import com.android.tools.r8.references.Reference;
 import com.android.tools.r8.shaking.ProguardConfiguration;
 import com.android.tools.r8.shaking.ProguardConfigurationRule;
 import com.android.tools.r8.utils.IROrdering.IdentityIROrdering;
@@ -30,6 +35,7 @@
 import com.google.common.base.Equivalence.Wrapper;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Sets;
 import java.nio.file.Path;
 import java.util.ArrayList;
 import java.util.Comparator;
@@ -452,6 +458,37 @@
     return assertionsEnabled;
   }
 
+  /** A set of dexitems we have reported missing to dedupe warnings. */
+  private final Set<DexItem> reportedMissingForDesugaring = Sets.newConcurrentHashSet();
+
+  public void warningMissingTypeForDesugar(
+      Origin origin, Position position, DexType missingType, DexType contextType) {
+    if (reportedMissingForDesugaring.add(missingType)) {
+      reporter.warning(
+          new InterfaceDesugarMissingTypeDiagnostic(
+              origin,
+              position,
+              Reference.classFromDescriptor(missingType.toDescriptorString()),
+              Reference.classFromDescriptor(contextType.toDescriptorString()),
+              null));
+    }
+  }
+
+  public void warningMissingInterfaceForDesugar(
+      DexClass classToDesugar, DexClass implementing, DexType missing) {
+    if (reportedMissingForDesugaring.add(missing)) {
+      reporter.warning(
+          new InterfaceDesugarMissingTypeDiagnostic(
+              classToDesugar.getOrigin(),
+              Position.UNKNOWN,
+              Reference.classFromDescriptor(missing.toDescriptorString()),
+              Reference.classFromDescriptor(classToDesugar.getType().toDescriptorString()),
+              classToDesugar == implementing
+                  ? null
+                  : Reference.classFromDescriptor(implementing.getType().toDescriptorString())));
+    }
+  }
+
   public void warningMissingEnclosingMember(DexType clazz, Origin origin, int version) {
     TypeVersionPair pair = new TypeVersionPair(version, clazz);
     synchronized (missingEnclosingMembers) {
diff --git a/src/test/java/com/android/tools/r8/TestCompileResult.java b/src/test/java/com/android/tools/r8/TestCompileResult.java
index d63626a..5444252 100644
--- a/src/test/java/com/android/tools/r8/TestCompileResult.java
+++ b/src/test/java/com/android/tools/r8/TestCompileResult.java
@@ -117,6 +117,7 @@
   }
 
   public CR addRunClasspathClasses(List<Class<?>> classpath) {
+    assert getBackend() == Backend.CF;
     try {
       Path path = state.getNewTempFolder().resolve("runtime-classes.jar");
       ArchiveConsumer consumer = new ArchiveConsumer(path);
diff --git a/src/test/java/com/android/tools/r8/desugar/DesugarMissingTypeClassTest.java b/src/test/java/com/android/tools/r8/desugar/DesugarMissingTypeClassTest.java
new file mode 100644
index 0000000..1269b34
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/DesugarMissingTypeClassTest.java
@@ -0,0 +1,113 @@
+// Copyright (c) 2019, 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.desugar;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.D8TestBuilder;
+import com.android.tools.r8.D8TestCompileResult;
+import com.android.tools.r8.Diagnostic;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestDiagnosticMessages;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.errors.DesugarDiagnostic;
+import com.android.tools.r8.errors.InterfaceDesugarMissingTypeDiagnostic;
+import com.android.tools.r8.position.Position;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.StringUtils;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class DesugarMissingTypeClassTest extends TestBase {
+
+  static final String EXPECTED = StringUtils.lines("Hello, world");
+
+  private final TestParameters parameters;
+
+  @Parameterized.Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimes().withAllApiLevels().build();
+  }
+
+  public DesugarMissingTypeClassTest(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  boolean supportsDefaultInterfaceMethods() {
+    return parameters.getRuntime().isCf()
+        || AndroidApiLevel.N.getLevel() <= parameters.getApiLevel().getLevel();
+  }
+
+  @Test
+  public void test() throws Exception {
+    if (parameters.isCfRuntime()) {
+      testForJvm()
+          .addProgramClasses(TestClass.class, MyClass.class, MissingInterface.class)
+          .run(parameters.getRuntime(), TestClass.class)
+          .assertSuccessWithOutput(EXPECTED);
+    } else {
+      D8TestBuilder builder =
+          testForD8()
+              .addProgramClasses(TestClass.class, MyClass.class)
+              .setMinApi(parameters.getApiLevel());
+      TestDiagnosticMessages messages = builder.getState().getDiagnosticsMessages();
+      D8TestCompileResult compileResult = builder.compile();
+      if (supportsDefaultInterfaceMethods()) {
+        messages.assertNoMessages();
+        compileResult
+            .addRunClasspathFiles(
+                testForD8()
+                    .setMinApi(parameters.getApiLevel())
+                    .addProgramClasses(MissingInterface.class)
+                    .compile()
+                    .writeToZip())
+            .run(parameters.getRuntime(), TestClass.class)
+            .assertSuccessWithOutput(EXPECTED);
+      } else {
+        messages.assertOnlyWarnings();
+        assertEquals(1, messages.getWarnings().size());
+        Diagnostic diagnostic = messages.getWarnings().get(0);
+        assertTrue(diagnostic instanceof DesugarDiagnostic);
+        assertTrue(diagnostic instanceof InterfaceDesugarMissingTypeDiagnostic);
+        InterfaceDesugarMissingTypeDiagnostic desugarWarning = (InterfaceDesugarMissingTypeDiagnostic) diagnostic;
+        assertEquals(
+            Reference.classFromClass(MissingInterface.class), desugarWarning.getMissingType());
+        assertEquals(Reference.classFromClass(MyClass.class), desugarWarning.getContextType());
+        assertEquals(Position.UNKNOWN, desugarWarning.getPosition());
+      }
+    }
+  }
+
+  public interface MissingInterface {
+    void foo();
+
+    default void bar() {
+      foo();
+    }
+  }
+
+  public static class MyClass implements MissingInterface {
+
+    @Override
+    public void foo() {
+      System.out.println("Hello, world");
+    }
+  }
+
+  static class TestClass {
+
+    public static void baz(MissingInterface fn) {
+      fn.bar();
+    }
+
+    public static void main(String[] args) {
+      baz(new MyClass());
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/DesugarMissingTypeLambdaTest.java b/src/test/java/com/android/tools/r8/desugar/DesugarMissingTypeLambdaTest.java
new file mode 100644
index 0000000..8f304c6
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/DesugarMissingTypeLambdaTest.java
@@ -0,0 +1,107 @@
+// Copyright (c) 2019, 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.desugar;
+
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.D8TestBuilder;
+import com.android.tools.r8.D8TestCompileResult;
+import com.android.tools.r8.Diagnostic;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestDiagnosticMessages;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.errors.DesugarDiagnostic;
+import com.android.tools.r8.errors.InterfaceDesugarMissingTypeDiagnostic;
+import com.android.tools.r8.position.Position;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.StringUtils;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class DesugarMissingTypeLambdaTest extends TestBase {
+
+  static final String EXPECTED = StringUtils.lines("Hello, world");
+
+  private final TestParameters parameters;
+
+  @Parameterized.Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimes().withAllApiLevels().build();
+  }
+
+  public DesugarMissingTypeLambdaTest(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  boolean supportsDefaultInterfaceMethods() {
+    return parameters.getRuntime().isCf()
+        || AndroidApiLevel.N.getLevel() <= parameters.getApiLevel().getLevel();
+  }
+
+  @Test
+  public void test() throws Exception {
+    if (parameters.isCfRuntime()) {
+      testForJvm()
+          .addProgramClasses(TestClass.class, MissingInterface.class)
+          .run(parameters.getRuntime(), TestClass.class)
+          .assertSuccessWithOutput(EXPECTED);
+    } else {
+      D8TestBuilder builder =
+          testForD8().addProgramClasses(TestClass.class).setMinApi(parameters.getApiLevel());
+      TestDiagnosticMessages messages = builder.getState().getDiagnosticsMessages();
+      D8TestCompileResult compileResult = builder.compile();
+      if (supportsDefaultInterfaceMethods()) {
+        messages.assertNoMessages();
+        compileResult
+            .addRunClasspathFiles(
+                testForD8()
+                    .setMinApi(parameters.getApiLevel())
+                    .addProgramClasses(MissingInterface.class)
+                    .compile()
+                    .writeToZip())
+            .run(parameters.getRuntime(), TestClass.class)
+            .assertSuccessWithOutput(EXPECTED);
+      } else {
+        messages.assertOnlyWarnings();
+        assertEquals(1, messages.getWarnings().size());
+        Diagnostic diagnostic = messages.getWarnings().get(0);
+        assertTrue(diagnostic instanceof DesugarDiagnostic);
+        assertTrue(diagnostic instanceof InterfaceDesugarMissingTypeDiagnostic);
+        InterfaceDesugarMissingTypeDiagnostic desugarWarning = (InterfaceDesugarMissingTypeDiagnostic) diagnostic;
+        assertEquals(
+            Reference.classFromClass(MissingInterface.class), desugarWarning.getMissingType());
+        // TODO(b/132671303): The context class should not be the synthesized lambda class.
+        assertThat(desugarWarning.getContextType().getDescriptor(), containsString("$$Lambda"));
+        // TODO(b/132671303): The position info should be the method context.
+        assertEquals(Position.UNKNOWN, desugarWarning.getPosition());
+      }
+    }
+  }
+
+  public interface MissingInterface {
+    void foo();
+
+    default void bar() {
+      foo();
+    }
+  }
+
+  static class TestClass {
+
+    public static void baz(MissingInterface fn) {
+      fn.bar();
+    }
+
+    public static void main(String[] args) {
+      baz(() -> System.out.println("Hello, world"));
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/DesugarMissingTypeStaticInvokeTest.java b/src/test/java/com/android/tools/r8/desugar/DesugarMissingTypeStaticInvokeTest.java
new file mode 100644
index 0000000..7422eb8
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/DesugarMissingTypeStaticInvokeTest.java
@@ -0,0 +1,100 @@
+// Copyright (c) 2019, 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.desugar;
+
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.D8TestBuilder;
+import com.android.tools.r8.D8TestCompileResult;
+import com.android.tools.r8.Diagnostic;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestDiagnosticMessages;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.errors.DesugarDiagnostic;
+import com.android.tools.r8.errors.InterfaceDesugarMissingTypeDiagnostic;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.StringUtils;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class DesugarMissingTypeStaticInvokeTest extends TestBase {
+
+  static final String EXPECTED = StringUtils.lines("Hello, world");
+
+  private final TestParameters parameters;
+
+  @Parameterized.Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimes().withAllApiLevels().build();
+  }
+
+  public DesugarMissingTypeStaticInvokeTest(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  boolean supportsDefaultInterfaceMethods() {
+    return parameters.getRuntime().isCf()
+        || AndroidApiLevel.N.getLevel() <= parameters.getApiLevel().getLevel();
+  }
+
+  @Test
+  public void test() throws Exception {
+    if (parameters.isCfRuntime()) {
+      testForJvm()
+          .addProgramClasses(TestClass.class, MissingInterface.class)
+          .run(parameters.getRuntime(), TestClass.class)
+          .assertSuccessWithOutput(EXPECTED);
+    } else {
+      D8TestBuilder builder =
+          testForD8().addProgramClasses(TestClass.class).setMinApi(parameters.getApiLevel());
+      TestDiagnosticMessages messages = builder.getState().getDiagnosticsMessages();
+      D8TestCompileResult compileResult = builder.compile();
+      if (supportsDefaultInterfaceMethods()) {
+        messages.assertNoMessages();
+        compileResult
+            .addRunClasspathFiles(
+                testForD8()
+                    .setMinApi(parameters.getApiLevel())
+                    .addProgramClasses(MissingInterface.class)
+                    .compile()
+                    .writeToZip())
+            .run(parameters.getRuntime(), TestClass.class)
+            .assertSuccessWithOutput(EXPECTED);
+      } else {
+        messages.assertOnlyWarnings();
+        assertEquals(1, messages.getWarnings().size());
+        Diagnostic diagnostic = messages.getWarnings().get(0);
+        assertTrue(diagnostic instanceof DesugarDiagnostic);
+        assertTrue(diagnostic instanceof InterfaceDesugarMissingTypeDiagnostic);
+        InterfaceDesugarMissingTypeDiagnostic desugarWarning = (InterfaceDesugarMissingTypeDiagnostic) diagnostic;
+        assertEquals(
+            Reference.classFromClass(MissingInterface.class), desugarWarning.getMissingType());
+        assertEquals(Reference.classFromClass(TestClass.class), desugarWarning.getContextType());
+        assertThat(
+            desugarWarning.getPosition().getDescription(),
+            containsString(TestClass.class.getTypeName() + ".main"));
+      }
+    }
+  }
+
+  public interface MissingInterface {
+    static void foo() {
+      System.out.println("Hello, world");
+    }
+  }
+
+  static class TestClass {
+
+    public static void main(String[] args) {
+      MissingInterface.foo();
+    }
+  }
+}