[Retrace] Narrowing by non-mapped position should give empty result

Fixes: 201982044
Bug: 202055473
Change-Id: Ie1018b08cc8bc8de5dec501a4d598b583d331089
diff --git a/src/main/java/com/android/tools/r8/retrace/RetraceClassResult.java b/src/main/java/com/android/tools/r8/retrace/RetraceClassResult.java
index 9f5bc71..c2bd31a 100644
--- a/src/main/java/com/android/tools/r8/retrace/RetraceClassResult.java
+++ b/src/main/java/com/android/tools/r8/retrace/RetraceClassResult.java
@@ -12,8 +12,6 @@
 @Keep
 public interface RetraceClassResult extends RetraceResult<RetraceClassElement> {
 
-  boolean hasRetraceResult();
-
   RetraceFieldResult lookupField(String fieldName);
 
   RetraceFieldResult lookupField(String fieldName, TypeReference fieldType);
diff --git a/src/main/java/com/android/tools/r8/retrace/RetraceResult.java b/src/main/java/com/android/tools/r8/retrace/RetraceResult.java
index 1bc6ed6..7ff9c4e 100644
--- a/src/main/java/com/android/tools/r8/retrace/RetraceResult.java
+++ b/src/main/java/com/android/tools/r8/retrace/RetraceResult.java
@@ -31,4 +31,6 @@
   default void forEach(Consumer<E> action) {
     stream().forEach(action);
   }
+
+  boolean isEmpty();
 }
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/RetraceClassResultImpl.java b/src/main/java/com/android/tools/r8/retrace/internal/RetraceClassResultImpl.java
index f354bb3..0d834a3 100644
--- a/src/main/java/com/android/tools/r8/retrace/internal/RetraceClassResultImpl.java
+++ b/src/main/java/com/android/tools/r8/retrace/internal/RetraceClassResultImpl.java
@@ -148,7 +148,7 @@
   }
 
   @Override
-  public boolean hasRetraceResult() {
+  public boolean isEmpty() {
     return mapper != null;
   }
 
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/RetraceFieldResultImpl.java b/src/main/java/com/android/tools/r8/retrace/internal/RetraceFieldResultImpl.java
index 38667c4..33abecc 100644
--- a/src/main/java/com/android/tools/r8/retrace/internal/RetraceFieldResultImpl.java
+++ b/src/main/java/com/android/tools/r8/retrace/internal/RetraceFieldResultImpl.java
@@ -92,6 +92,11 @@
     return mappings.size() > 1;
   }
 
+  @Override
+  public boolean isEmpty() {
+    return memberNamings == null || memberNamings.isEmpty();
+  }
+
   public static class ElementImpl implements RetraceFieldElement {
 
     private final RetracedFieldReferenceImpl fieldReference;
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/RetraceFrameResultImpl.java b/src/main/java/com/android/tools/r8/retrace/internal/RetraceFrameResultImpl.java
index d3b75bc..ee295e2 100644
--- a/src/main/java/com/android/tools/r8/retrace/internal/RetraceFrameResultImpl.java
+++ b/src/main/java/com/android/tools/r8/retrace/internal/RetraceFrameResultImpl.java
@@ -219,6 +219,11 @@
             () -> mappedRange.getOriginalLineNumber(obfuscatedPosition.getAsInt())));
   }
 
+  @Override
+  public boolean isEmpty() {
+    return !mappedRanges.isEmpty();
+  }
+
   public static class ElementImpl implements RetraceFrameElement {
 
     private final RetracedMethodReferenceImpl methodReference;
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/RetraceMethodResultImpl.java b/src/main/java/com/android/tools/r8/retrace/internal/RetraceMethodResultImpl.java
index 10ce889..60a6967 100644
--- a/src/main/java/com/android/tools/r8/retrace/internal/RetraceMethodResultImpl.java
+++ b/src/main/java/com/android/tools/r8/retrace/internal/RetraceMethodResultImpl.java
@@ -65,6 +65,11 @@
   }
 
   @Override
+  public boolean isEmpty() {
+    return mappedRanges == null || mappedRanges.isEmpty();
+  }
+
+  @Override
   public RetraceFrameResultImpl narrowByPosition(
       RetraceStackTraceContext context, OptionalInt position) {
     List<Pair<RetraceClassElementImpl, List<MappedRange>>> narrowedRanges = new ArrayList<>();
@@ -79,36 +84,43 @@
       }
       MappedRangesOfName mappedRangesOfElement = new MappedRangesOfName(mappedRange.getSecond());
       List<MappedRange> mappedRangesForPosition = null;
-      if (position.isPresent() && position.getAsInt() >= 0) {
+      boolean hasPosition = position.isPresent() && position.getAsInt() >= 0;
+      if (hasPosition) {
         mappedRangesForPosition =
             mappedRangesOfElement.allRangesForLine(position.getAsInt(), false);
       }
       if (mappedRangesForPosition == null || mappedRangesForPosition.isEmpty()) {
-        mappedRangesForPosition = mappedRangesOfElement.getMappedRanges();
-      } else if (stackTraceContext != null && stackTraceContext.hasRewritePosition()) {
-        List<OutlineCallsiteMappingInformation> outlineCallsiteInformation =
-            ListUtils.last(mappedRangesForPosition).getOutlineCallsiteInformation();
-        if (!outlineCallsiteInformation.isEmpty()) {
-          assert outlineCallsiteInformation.size() == 1
-              : "There can only be one outline entry for a line";
-          return narrowByPosition(
-              stackTraceContext.buildFromThis().clearRewritePosition().build(),
-              OptionalInt.of(
-                  outlineCallsiteInformation
-                      .get(0)
-                      .rewritePosition(stackTraceContext.getRewritePosition())));
-        }
+        mappedRangesForPosition =
+            hasPosition
+                ? ListUtils.filter(
+                    mappedRangesOfElement.getMappedRanges(), range -> range.minifiedRange == null)
+                : mappedRangesOfElement.getMappedRanges();
       }
-      assert mappedRangesForPosition != null && !mappedRangesForPosition.isEmpty();
-      // Mapped ranges can have references to overloaded signatures. We distinguish those by looking
-      // at the cardinal mapping range.
-      for (MappedRange mappedRangeForPosition : mappedRangesForPosition) {
-        if (narrowedRanges.isEmpty()
-            || mappedRangeForPosition.originalRange == null
-            || !mappedRangeForPosition.originalRange.isCardinal) {
-          narrowedRanges.add(new Pair<>(mappedRange.getFirst(), new ArrayList<>()));
+      if (mappedRangesForPosition != null && !mappedRangesForPosition.isEmpty()) {
+        if (stackTraceContext != null && stackTraceContext.hasRewritePosition()) {
+          List<OutlineCallsiteMappingInformation> outlineCallsiteInformation =
+              ListUtils.last(mappedRangesForPosition).getOutlineCallsiteInformation();
+          if (!outlineCallsiteInformation.isEmpty()) {
+            assert outlineCallsiteInformation.size() == 1
+                : "There can only be one outline entry for a line";
+            return narrowByPosition(
+                stackTraceContext.buildFromThis().clearRewritePosition().build(),
+                OptionalInt.of(
+                    outlineCallsiteInformation
+                        .get(0)
+                        .rewritePosition(stackTraceContext.getRewritePosition())));
+          }
         }
-        ListUtils.last(narrowedRanges).getSecond().add(mappedRangeForPosition);
+        // Mapped ranges can have references to overloaded signatures. We distinguish those by
+        // looking at the cardinal mapping range.
+        for (MappedRange mappedRangeForPosition : mappedRangesForPosition) {
+          if (narrowedRanges.isEmpty()
+              || mappedRangeForPosition.originalRange == null
+              || !mappedRangeForPosition.originalRange.isCardinal) {
+            narrowedRanges.add(new Pair<>(mappedRange.getFirst(), new ArrayList<>()));
+          }
+          ListUtils.last(narrowedRanges).getSecond().add(mappedRangeForPosition);
+        }
       }
     }
     return new RetraceFrameResultImpl(
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/RetraceThrownExceptionResultImpl.java b/src/main/java/com/android/tools/r8/retrace/internal/RetraceThrownExceptionResultImpl.java
index aa2c1f0..bb49d70 100644
--- a/src/main/java/com/android/tools/r8/retrace/internal/RetraceThrownExceptionResultImpl.java
+++ b/src/main/java/com/android/tools/r8/retrace/internal/RetraceThrownExceptionResultImpl.java
@@ -34,6 +34,11 @@
     return Stream.of(createElement());
   }
 
+  @Override
+  public boolean isEmpty() {
+    return obfuscatedReference == null;
+  }
+
   private RetraceThrownExceptionElement createElement() {
     return new RetraceThrownExceptionElementImpl(
         this,
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/StackTraceElementProxyRetracerImpl.java b/src/main/java/com/android/tools/r8/retrace/internal/StackTraceElementProxyRetracerImpl.java
index 5623bf2..987aead 100644
--- a/src/main/java/com/android/tools/r8/retrace/internal/StackTraceElementProxyRetracerImpl.java
+++ b/src/main/java/com/android/tools/r8/retrace/internal/StackTraceElementProxyRetracerImpl.java
@@ -131,6 +131,25 @@
                                   ? OptionalInt.of(element.getLineNumber())
                                   : OptionalInt.empty(),
                               element.getMethodName());
+                      if (!frameResult.isEmpty()) {
+                        return classResult.stream()
+                            .map(
+                                classElement ->
+                                    proxy
+                                        .builder()
+                                        .setTopFrame(true)
+                                        .joinAmbiguous(classResult.isAmbiguous())
+                                        .setRetracedClass(classElement.getRetracedClass())
+                                        .applyIf(
+                                            element.hasLineNumber(),
+                                            b -> b.setLineNumber(element.getLineNumber()))
+                                        .apply(
+                                            setSourceFileOnProxy(
+                                                classElement::getSourceFile,
+                                                classElement.getRetracedClass(),
+                                                classResult))
+                                        .build());
+                      }
                       return frameResult.stream()
                           .flatMap(
                               frameElement -> {
@@ -230,9 +249,7 @@
           retracedSourceFile.hasRetraceResult()
               ? retracedSourceFile.getSourceFile()
               : RetraceUtils.inferSourceFile(
-                  classReference.getTypeName(),
-                  original.getSourceFile(),
-                  classResult.hasRetraceResult()));
+                  classReference.getTypeName(), original.getSourceFile(), classResult.isEmpty()));
     };
   }
 
diff --git a/src/test/java/com/android/tools/r8/retrace/StackTraceRegularExpressionParserTests.java b/src/test/java/com/android/tools/r8/retrace/StackTraceRegularExpressionParserTests.java
index fc0aaea..b769752 100644
--- a/src/test/java/com/android/tools/r8/retrace/StackTraceRegularExpressionParserTests.java
+++ b/src/test/java/com/android/tools/r8/retrace/StackTraceRegularExpressionParserTests.java
@@ -517,12 +517,12 @@
 
           @Override
           public List<String> retracedStackTrace() {
-            return ImmutableList.of("com.android.tools.r8.R8.foo(7)");
+            return ImmutableList.of("com.android.tools.r8.R8.a(42)");
           }
 
           @Override
           public List<String> retraceVerboseStackTrace() {
-            return ImmutableList.of("com.android.tools.r8.R8.boolean foo()(7)");
+            return ImmutableList.of("com.android.tools.r8.R8.a(42)");
           }
 
           @Override
diff --git a/src/test/java/com/android/tools/r8/retrace/api/RetraceApiOutsideLineRangeTest.java b/src/test/java/com/android/tools/r8/retrace/api/RetraceApiOutsideLineRangeTest.java
index 714719a..a3ba721 100644
--- a/src/test/java/com/android/tools/r8/retrace/api/RetraceApiOutsideLineRangeTest.java
+++ b/src/test/java/com/android/tools/r8/retrace/api/RetraceApiOutsideLineRangeTest.java
@@ -61,8 +61,7 @@
       Retracer retracer =
           Retracer.createDefault(
               ProguardMapProducer.fromString(mapping), new DiagnosticsHandler() {});
-      // TODO(b/201982044): Should be the empty set.
-      retraceClassMethodAndPosition(retracer, someClassRenamed, someClassOriginal, 2, 4);
+      retraceClassMethodAndPosition(retracer, someClassRenamed, someClassOriginal, 2, 0);
       retraceClassMethodAndPosition(retracer, someOtherClassRenamed, someOtherClassOriginal, 1, 1);
     }
 
diff --git a/src/test/java/com/android/tools/r8/retrace/stacktraces/OutsideLineRangeStackTraceTest.java b/src/test/java/com/android/tools/r8/retrace/stacktraces/OutsideLineRangeStackTraceTest.java
index c6f7509..81587cf 100644
--- a/src/test/java/com/android/tools/r8/retrace/stacktraces/OutsideLineRangeStackTraceTest.java
+++ b/src/test/java/com/android/tools/r8/retrace/stacktraces/OutsideLineRangeStackTraceTest.java
@@ -34,41 +34,26 @@
   public List<String> retracedStackTrace() {
     return Arrays.asList(
         "java.io.IOException: INVALID_SENDER",
-        // TODO(b/201982044): Should not be ambiguous since b.a(:27) should be b.a(:27)
         "\tat some.other.Class.method1(Class.java:42)",
         "\tat some.other.Class.method1(Class.java:42)",
-        "\tat some.Class.method2(Class.java:10)",
-        "\tat some.Class.method2(Class.java)",
-        "<OR> java.io.IOException: INVALID_SENDER",
-        "\tat some.other.Class.method1(Class.java:42)",
-        "\tat some.other.Class.method1(Class.java:42)",
-        "\tat some.Class.method2(Class.java:27)",
+        "\tat some.Class.a(Class.java:27)",
         "\tat some.Class.method2(Class.java)");
   }
 
   @Override
   public List<String> retraceVerboseStackTrace() {
     return Arrays.asList(
-        "There are 4 ambiguous stack traces.",
+        "There are 2 ambiguous stack traces.",
         "java.io.IOException: INVALID_SENDER",
         "\tat some.other.Class.void method1()(Class.java:42)",
         "\tat some.other.Class.void method1()(Class.java:42)",
-        "\tat some.Class.void method2()(Class.java:10)",
+        "\tat some.Class.a(Class.java:27)",
+        // TODO(b/202055473): Could emit range 11-13.
         "\tat some.Class.void method2()(Class.java)",
         "<OR> java.io.IOException: INVALID_SENDER",
         "\tat some.other.Class.void method1()(Class.java:42)",
         "\tat some.other.Class.void method1()(Class.java:42)",
-        "\tat some.Class.void method2()(Class.java:10)",
-        "\tat some.Class.void method2()(Class.java:10)",
-        "<OR> java.io.IOException: INVALID_SENDER",
-        "\tat some.other.Class.void method1()(Class.java:42)",
-        "\tat some.other.Class.void method1()(Class.java:42)",
-        "\tat some.Class.void method2()(Class.java:27)",
-        "\tat some.Class.void method2()(Class.java)",
-        "<OR> java.io.IOException: INVALID_SENDER",
-        "\tat some.other.Class.void method1()(Class.java:42)",
-        "\tat some.other.Class.void method1()(Class.java:42)",
-        "\tat some.Class.void method2()(Class.java:27)",
+        "\tat some.Class.a(Class.java:27)",
         "\tat some.Class.void method2()(Class.java:10)");
   }
 
diff --git a/third_party/retrace/binary_compatibility.tar.gz.sha1 b/third_party/retrace/binary_compatibility.tar.gz.sha1
index 67554d1..f81f5fc 100644
--- a/third_party/retrace/binary_compatibility.tar.gz.sha1
+++ b/third_party/retrace/binary_compatibility.tar.gz.sha1
@@ -1 +1 @@
-ef1d22b8680bd9bc362c017a5641524ffef4d22e
\ No newline at end of file
+239b7652dc8282715373a1871987c3919df029d6
\ No newline at end of file