[Retrace] Treat retraced position 0 as no line

Bug: b/232212653
Bug: b/255705077
Change-Id: I008f1c4405def576891df613863ba94aca602935
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 f82327a..2deef6f 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
@@ -228,24 +228,28 @@
     OptionalInt originalPosition = mappedRangeForFrame.position;
     if (!isAmbiguous()
         && (mappedRange.minifiedRange == null || obfuscatedPosition.orElse(-1) == -1)) {
-      int originalLineNumber = mappedRange.getFirstPositionOfOriginalRange(0);
-      if (originalLineNumber > 0) {
-        return RetracedMethodReferenceImpl.create(
-            methodReference, OptionalUtils.orElse(originalPosition, originalLineNumber));
-      } else {
-        return RetracedMethodReferenceImpl.create(methodReference, originalPosition);
-      }
+      return RetracedMethodReferenceImpl.create(
+          methodReference,
+          OptionalUtils.map(
+              originalPosition,
+              () -> {
+                int originalLineNumber = mappedRange.getFirstPositionOfOriginalRange(0);
+                return originalLineNumber > 0
+                    ? OptionalInt.of(originalLineNumber)
+                    : OptionalInt.empty();
+              }));
     }
-    if (!obfuscatedPosition.isPresent()
+    if (obfuscatedPosition.isEmpty()
         || mappedRange.minifiedRange == null
         || !mappedRange.minifiedRange.contains(obfuscatedPosition.getAsInt())) {
       return RetracedMethodReferenceImpl.create(methodReference, originalPosition);
     }
     return RetracedMethodReferenceImpl.create(
         methodReference,
-        OptionalUtils.orElseGet(
+        OptionalUtils.map(
             originalPosition,
-            () -> mappedRange.getOriginalLineNumber(obfuscatedPosition.getAsInt())));
+            () ->
+                OptionalInt.of(mappedRange.getOriginalLineNumber(obfuscatedPosition.getAsInt()))));
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/StackTraceElementStringProxy.java b/src/main/java/com/android/tools/r8/retrace/internal/StackTraceElementStringProxy.java
index 761a124..b88958e 100644
--- a/src/main/java/com/android/tools/r8/retrace/internal/StackTraceElementStringProxy.java
+++ b/src/main/java/com/android/tools/r8/retrace/internal/StackTraceElementStringProxy.java
@@ -115,6 +115,9 @@
     }
     try {
       String lineNumberString = getEntryInLine(lineNumber);
+      if (lineNumberString.startsWith(":")) {
+        lineNumberString = lineNumberString.substring(1);
+      }
       if (lineNumberString.isEmpty()) {
         return -1;
       }
@@ -238,14 +241,15 @@
               startIndex,
               endIndex,
               (retraced, original, verbose) -> {
-                boolean printLineNumber =
-                    retraced.hasLineNumber()
-                        && ((original.hasLineNumber() && original.getLineNumber() > -1)
-                            || !retraced.isAmbiguous()
-                            || verbose);
-                return printLineNumber
-                    ? ((insertSeparatorForRetraced ? ":" : "") + retraced.getLineNumber())
-                    : original.lineNumberAsString();
+                if (retraced.hasLineNumber()
+                    && ((original.hasLineNumber() && original.getLineNumber() > -1)
+                        || !retraced.isAmbiguous()
+                        || verbose)) {
+                  return retraced.getLineNumber() <= 0
+                      ? ""
+                      : ((insertSeparatorForRetraced ? ":" : "") + retraced.getLineNumber());
+                }
+                return original.lineNumberAsString();
               });
       orderedIndices.add(lineNumber);
       return this;
@@ -326,6 +330,10 @@
       }
       lastSeenStartIndex = newStartIndex;
     }
+
+    public String getLine() {
+      return line;
+    }
   }
 
   static class StringIndex {
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/StackTraceRegularExpressionParser.java b/src/main/java/com/android/tools/r8/retrace/internal/StackTraceRegularExpressionParser.java
index bf9123e..b73c9bc 100644
--- a/src/main/java/com/android/tools/r8/retrace/internal/StackTraceRegularExpressionParser.java
+++ b/src/main/java/com/android/tools/r8/retrace/internal/StackTraceRegularExpressionParser.java
@@ -297,7 +297,15 @@
         if (startOfGroup == NO_MATCH) {
           return false;
         }
-        builder.registerLineNumber(startOfGroup, matcher.end(captureGroup), false);
+        boolean insertSeparatorForRetraced = false;
+        // We need to include ':' in the group since we may want to rewrite '(SourceFile:0)` into
+        // (SourceFile) and not (SourceFile:)
+        if (startOfGroup > 0 && builder.getLine().charAt(startOfGroup + -1) == ':') {
+          startOfGroup = startOfGroup - 1;
+          insertSeparatorForRetraced = true;
+        }
+        int end = matcher.end(captureGroup);
+        builder.registerLineNumber(startOfGroup, end, insertSeparatorForRetraced);
         return true;
       };
     }
@@ -321,9 +329,10 @@
         int sourceFileEnd = startOfGroup + endOfSourceFileInGroup;
         builder.registerSourceFile(startOfGroup, sourceFileEnd);
         int endOfMatch = matcher.end(captureGroup);
-        int lineNumberStart = sourceFileEnd + 1;
-        builder.registerLineNumber(
-            Integer.min(lineNumberStart, endOfMatch), endOfMatch, lineNumberStart > endOfMatch);
+        // We need to include ':' in the group since we may want to rewrite '(SourceFile:0)` into
+        // (SourceFile) and not (SourceFile:). We fix this by setting the start of the linenumber
+        // group to the end of the SourceFile group and then force inserting ':'.
+        builder.registerLineNumber(Integer.min(sourceFileEnd, endOfMatch), endOfMatch, true);
         return true;
       };
     }
diff --git a/src/main/java/com/android/tools/r8/utils/OptionalUtils.java b/src/main/java/com/android/tools/r8/utils/OptionalUtils.java
index 974bf0c..925ca07 100644
--- a/src/main/java/com/android/tools/r8/utils/OptionalUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/OptionalUtils.java
@@ -9,11 +9,8 @@
 
 public class OptionalUtils {
 
-  public static OptionalInt orElse(OptionalInt optional, int orElse) {
-    return optional.isPresent() ? optional : OptionalInt.of(orElse);
+  public static OptionalInt map(OptionalInt optional, Supplier<OptionalInt> orElse) {
+    return optional.isPresent() ? optional : orElse.get();
   }
 
-  public static OptionalInt orElseGet(OptionalInt optional, Supplier<Integer> orElse) {
-    return optional.isPresent() ? optional : OptionalInt.of(orElse.get());
-  }
 }
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/MergedConstructorWithEquivalenceStackTraceTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/MergedConstructorWithEquivalenceStackTraceTest.java
index c39d3ef..655e6a8 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/MergedConstructorWithEquivalenceStackTraceTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/MergedConstructorWithEquivalenceStackTraceTest.java
@@ -60,7 +60,8 @@
                       // TODO(b/124483578): Stack trace lines from the merging of equivalent
                       //  constructors should retrace to the set of lines from each of the
                       //  individual source constructors.
-                      .map(1, stackTraceLine -> stackTraceLine.builderOf().setLineNumber(0).build())
+                      .map(
+                          1, stackTraceLine -> stackTraceLine.builderOf().setLineNumber(-1).build())
                       .build();
               assertThat(stackTrace, isSame(expectedStackTraceWithMergedConstructor));
               assertThat(codeInspector.clazz(B.class), not(isPresent()));
diff --git a/src/test/java/com/android/tools/r8/debuginfo/InliningWithoutPositionsTestRunner.java b/src/test/java/com/android/tools/r8/debuginfo/InliningWithoutPositionsTestRunner.java
index c164fa7..ba0dba3 100644
--- a/src/test/java/com/android/tools/r8/debuginfo/InliningWithoutPositionsTestRunner.java
+++ b/src/test/java/com/android/tools/r8/debuginfo/InliningWithoutPositionsTestRunner.java
@@ -150,7 +150,7 @@
         .setClassName(TEST_CLASS)
         .setFileName(TEST_FILE)
         .setMethodName(methodName)
-        .setLineNumber(hasPosition ? location.line : 0)
+        .setLineNumber(hasPosition ? location.line : -1)
         .build();
   }
 }
diff --git a/src/test/java/com/android/tools/r8/debuginfo/NoLineInfoTest.java b/src/test/java/com/android/tools/r8/debuginfo/NoLineInfoTest.java
index f0d4c89..3831e44 100644
--- a/src/test/java/com/android/tools/r8/debuginfo/NoLineInfoTest.java
+++ b/src/test/java/com/android/tools/r8/debuginfo/NoLineInfoTest.java
@@ -207,17 +207,16 @@
   private StackTrace getUnexpectedRetracedStacktrace() {
     assertFalse(parameters.isCfRuntime());
     StackTraceLine fooLine;
-    if (customSourceFile) {
-      // TODO(b/232212653): Should retrace convert out of "0" and represent it as <noline>/-1?
-      fooLine = inputLine("foo", 0);
-    } else if (isRuntimeWithPcAsLineNumberSupport()) {
+    if (isRuntimeWithPcAsLineNumberSupport() && !customSourceFile) {
       // TODO(b/232212653): Retrace should convert PC 1 to line <noline>/-1/0.
       fooLine = inputLine("foo", 1);
     } else {
       fooLine = inputLine("foo", -1);
     }
-    StackTraceLine barLine = inputLine("bar", getPcEncoding(0));
-    StackTraceLine bazLine = inputLine("baz", getPcEncoding(0));
+    int position =
+        isCompileWithPcAsLineNumberSupport() && !customSourceFile ? -1 : getPcEncoding(0);
+    StackTraceLine barLine = inputLine("bar", position);
+    StackTraceLine bazLine = inputLine("baz", position);
     return StackTrace.builder()
         .add(fooLine)
         .add(barLine)
diff --git a/src/test/java/com/android/tools/r8/debuginfo/OverloadWithNoLineNumberTest.java b/src/test/java/com/android/tools/r8/debuginfo/OverloadWithNoLineNumberTest.java
index 3695c0e..d0a41c5 100644
--- a/src/test/java/com/android/tools/r8/debuginfo/OverloadWithNoLineNumberTest.java
+++ b/src/test/java/com/android/tools/r8/debuginfo/OverloadWithNoLineNumberTest.java
@@ -65,8 +65,8 @@
                   String className = typeName(SimpleCallChainClassWithOverloads.class);
                   assertEquals(
                       StringUtils.joinLines(
-                          "\tat " + className + ".void test(long)(" + SOURCE_FILE_NAME + ":0)",
-                          "\tat " + className + ".void test()(" + SOURCE_FILE_NAME + ":0)",
+                          "\tat " + className + ".void test(long)(" + SOURCE_FILE_NAME + ")",
+                          "\tat " + className + ".void test()(" + SOURCE_FILE_NAME + ")",
                           "\tat "
                               + className
                               + ".void main(java.lang.String[])("
diff --git a/src/test/java/com/android/tools/r8/retrace/OverloadsWithoutLineNumberTest.java b/src/test/java/com/android/tools/r8/retrace/OverloadsWithoutLineNumberTest.java
index cbe2680..b1c1b96 100644
--- a/src/test/java/com/android/tools/r8/retrace/OverloadsWithoutLineNumberTest.java
+++ b/src/test/java/com/android/tools/r8/retrace/OverloadsWithoutLineNumberTest.java
@@ -66,7 +66,7 @@
     assertEquals(
         "\tat "
             + typeName(ClassWithOverload.class)
-            + ".void test(int)(OverloadsWithoutLineNumberTest.java:0)",
+            + ".void test(int)(OverloadsWithoutLineNumberTest.java)",
         box.get().get(1));
   }
 
diff --git a/src/test/java/com/android/tools/r8/retrace/stacktraces/InlineFileNameStackTrace.java b/src/test/java/com/android/tools/r8/retrace/stacktraces/InlineFileNameStackTrace.java
index 048b941..b8243aa 100644
--- a/src/test/java/com/android/tools/r8/retrace/stacktraces/InlineFileNameStackTrace.java
+++ b/src/test/java/com/android/tools/r8/retrace/stacktraces/InlineFileNameStackTrace.java
@@ -21,7 +21,7 @@
   public List<String> retracedStackTrace() {
     return Arrays.asList(
         "Exception in thread \"main\" java.lang.NullPointerException",
-        "\tat foo.Bar$Baz.baz(Bar.java:0)",
+        "\tat foo.Bar$Baz.baz(Bar.java)",
         "\tat Foo$Bar.bar(Foo.java:2)",
         "\tat com.android.tools.r8.naming.retrace.Main$Foo.method1(Main.java:8)",
         "\tat com.android.tools.r8.naming.retrace.Main.main(Main.java:7)");
@@ -31,7 +31,7 @@
   public List<String> retraceVerboseStackTrace() {
     return Arrays.asList(
         "Exception in thread \"main\" java.lang.NullPointerException",
-        "\tat foo.Bar$Baz.void baz(long)(Bar.java:0)",
+        "\tat foo.Bar$Baz.void baz(long)(Bar.java)",
         "\tat Foo$Bar.void bar(int)(Foo.java:2)",
         "\tat com.android.tools.r8.naming.retrace.Main$Foo"
             + ".void method1(java.lang.String)(Main.java:8)",
diff --git a/src/test/java/com/android/tools/r8/retrace/stacktraces/InlineFileNameWithInnerClassesStackTrace.java b/src/test/java/com/android/tools/r8/retrace/stacktraces/InlineFileNameWithInnerClassesStackTrace.java
index 177eb8a..1c53b40 100644
--- a/src/test/java/com/android/tools/r8/retrace/stacktraces/InlineFileNameWithInnerClassesStackTrace.java
+++ b/src/test/java/com/android/tools/r8/retrace/stacktraces/InlineFileNameWithInnerClassesStackTrace.java
@@ -21,7 +21,7 @@
   public List<String> retracedStackTrace() {
     return Arrays.asList(
         "Exception in thread \"main\" java.lang.NullPointerException",
-        "\tat foo.Bar$Baz$Qux.baz(Bar.java:0)",
+        "\tat foo.Bar$Baz$Qux.baz(Bar.java)",
         "\tat com.android.tools.r8.naming.retrace.Main.main(Main.java:7)");
   }
 
@@ -29,7 +29,7 @@
   public List<String> retraceVerboseStackTrace() {
     return Arrays.asList(
         "Exception in thread \"main\" java.lang.NullPointerException",
-        "\tat foo.Bar$Baz$Qux.void baz(long)(Bar.java:0)",
+        "\tat foo.Bar$Baz$Qux.void baz(long)(Bar.java)",
         "\tat com.android.tools.r8.naming.retrace.Main.void main(java.lang.String[])(Main.java:7)");
   }
 
diff --git a/src/test/java/com/android/tools/r8/retrace/stacktraces/InlineNoLineNumberStackTrace.java b/src/test/java/com/android/tools/r8/retrace/stacktraces/InlineNoLineNumberStackTrace.java
index 763ec80..28e94c5 100644
--- a/src/test/java/com/android/tools/r8/retrace/stacktraces/InlineNoLineNumberStackTrace.java
+++ b/src/test/java/com/android/tools/r8/retrace/stacktraces/InlineNoLineNumberStackTrace.java
@@ -21,20 +21,20 @@
   public List<String> retracedStackTrace() {
     return Arrays.asList(
         "Exception in thread \"main\" java.lang.NullPointerException",
-        "\tat com.android.tools.r8.naming.retrace.Main.method3(Main.java:0)",
-        "\tat com.android.tools.r8.naming.retrace.Main.method2(Main.java:0)",
-        "\tat com.android.tools.r8.naming.retrace.Main.method1(Main.java:0)",
-        "\tat com.android.tools.r8.naming.retrace.Main.main(Main.java:0)");
+        "\tat com.android.tools.r8.naming.retrace.Main.method3(Main.java)",
+        "\tat com.android.tools.r8.naming.retrace.Main.method2(Main.java)",
+        "\tat com.android.tools.r8.naming.retrace.Main.method1(Main.java)",
+        "\tat com.android.tools.r8.naming.retrace.Main.main(Main.java)");
   }
 
   @Override
   public List<String> retraceVerboseStackTrace() {
     return Arrays.asList(
         "Exception in thread \"main\" java.lang.NullPointerException",
-        "\tat com.android.tools.r8.naming.retrace.Main.void method3(long)(Main.java:0)",
-        "\tat com.android.tools.r8.naming.retrace.Main.void method2(int)(Main.java:0)",
-        "\tat com.android.tools.r8.naming.retrace.Main.void method1(java.lang.String)(Main.java:0)",
-        "\tat com.android.tools.r8.naming.retrace.Main.void main(java.lang.String[])(Main.java:0)");
+        "\tat com.android.tools.r8.naming.retrace.Main.void method3(long)(Main.java)",
+        "\tat com.android.tools.r8.naming.retrace.Main.void method2(int)(Main.java)",
+        "\tat com.android.tools.r8.naming.retrace.Main.void method1(java.lang.String)(Main.java)",
+        "\tat com.android.tools.r8.naming.retrace.Main.void main(java.lang.String[])(Main.java)");
   }
 
   @Override
diff --git a/src/test/java/com/android/tools/r8/retrace/stacktraces/NoObfuscationRangeMappingWithStackTrace.java b/src/test/java/com/android/tools/r8/retrace/stacktraces/NoObfuscationRangeMappingWithStackTrace.java
index 8ec36a8..67a0d43 100644
--- a/src/test/java/com/android/tools/r8/retrace/stacktraces/NoObfuscationRangeMappingWithStackTrace.java
+++ b/src/test/java/com/android/tools/r8/retrace/stacktraces/NoObfuscationRangeMappingWithStackTrace.java
@@ -26,8 +26,8 @@
         "Exception in thread \"main\" java.lang.NullPointerException",
         "\tat com.android.tools.r8.naming.retrace.Main.foo(Main.java:1)",
         "\tat com.android.tools.r8.naming.retrace.Main.bar(Main.java:3)",
-        "\tat com.android.tools.r8.naming.retrace.Main.baz(Main.java:0)",
-        "\tat com.android.tools.r8.naming.retrace.Main.main(Main.java:0)");
+        "\tat com.android.tools.r8.naming.retrace.Main.baz(Main.java)",
+        "\tat com.android.tools.r8.naming.retrace.Main.main(Main.java)");
   }
 
   @Override
@@ -36,8 +36,8 @@
         "Exception in thread \"main\" java.lang.NullPointerException",
         "\tat com.android.tools.r8.naming.retrace.Main.void foo(long)(Main.java:1)",
         "\tat com.android.tools.r8.naming.retrace.Main.void bar(int)(Main.java:3)",
-        "\tat com.android.tools.r8.naming.retrace.Main.void baz()(Main.java:0)",
-        "\tat com.android.tools.r8.naming.retrace.Main.void main(java.lang.String[])(Main.java:0)");
+        "\tat com.android.tools.r8.naming.retrace.Main.void baz()(Main.java)",
+        "\tat com.android.tools.r8.naming.retrace.Main.void main(java.lang.String[])(Main.java)");
   }
 
   @Override
diff --git a/src/test/java/com/android/tools/r8/retrace/stacktraces/PreambleLineNumberStackTrace.java b/src/test/java/com/android/tools/r8/retrace/stacktraces/PreambleLineNumberStackTrace.java
index 98a6ed6..bbd72bd 100644
--- a/src/test/java/com/android/tools/r8/retrace/stacktraces/PreambleLineNumberStackTrace.java
+++ b/src/test/java/com/android/tools/r8/retrace/stacktraces/PreambleLineNumberStackTrace.java
@@ -33,7 +33,7 @@
     return Arrays.asList(
         "Exception in thread \"main\" java.lang.NullPointerException",
         "  at kotlin.ResultKt.createFailure(Result.kt)",
-        "  at kotlin.ResultKt.createFailure(Result.kt:0)",
+        "  at kotlin.ResultKt.createFailure(Result.kt)",
         "  at kotlin.ResultKt.createFailure(Result.kt:122)",
         "  at kotlin.ResultKt.createFailure(Result.kt:124)");
   }
@@ -43,7 +43,7 @@
     return Arrays.asList(
         "Exception in thread \"main\" java.lang.NullPointerException",
         "  at kotlin.ResultKt.void createFailure(java.lang.Throwable)(Result.kt)",
-        "  at kotlin.ResultKt.void createFailure(java.lang.Throwable)(Result.kt:0)",
+        "  at kotlin.ResultKt.void createFailure(java.lang.Throwable)(Result.kt)",
         "  at kotlin.ResultKt.void createFailure(java.lang.Throwable)(Result.kt:122)",
         "  at kotlin.ResultKt.void createFailure(java.lang.Throwable)(Result.kt:124)");
   }