From 0606cf05bbc2f651296e685ea06eae100c74f394 Mon Sep 17 00:00:00 2001
From: George Dietrich <george@dietrich.app>
Date: Thu, 10 Oct 2024 03:15:10 -0700
Subject: [PATCH] Implement `codecov` format for `unreachable` tool (#15059)

---
 src/compiler/crystal/tools/unreachable.cr | 29 ++++++++++++++++++++++-
 1 file changed, 28 insertions(+), 1 deletion(-)

diff --git a/src/compiler/crystal/tools/unreachable.cr b/src/compiler/crystal/tools/unreachable.cr
index 733a94518899..8455a4186882 100644
--- a/src/compiler/crystal/tools/unreachable.cr
+++ b/src/compiler/crystal/tools/unreachable.cr
@@ -6,7 +6,7 @@ require "csv"
 module Crystal
   class Command
     private def unreachable
-      config, result = compile_no_codegen "tool unreachable", path_filter: true, unreachable_command: true, allowed_formats: %w[text json csv]
+      config, result = compile_no_codegen "tool unreachable", path_filter: true, unreachable_command: true, allowed_formats: %w[text json csv codecov]
 
       unreachable = UnreachableVisitor.new
 
@@ -42,6 +42,8 @@ module Crystal
         to_json(STDOUT)
       when "csv"
         to_csv(STDOUT)
+      when "codecov"
+        to_codecov(STDOUT)
       else
         to_text(STDOUT)
       end
@@ -111,6 +113,31 @@ module Crystal
         end
       end
     end
+
+    # https://docs.codecov.com/docs/codecov-custom-coverage-format
+    def to_codecov(io)
+      hits = Hash(String, Hash(Int32, Int32)).new { |hash, key| hash[key] = Hash(Int32, Int32).new(0) }
+
+      each do |a_def, location, count|
+        hits[location.filename][location.line_number] = count
+      end
+
+      JSON.build io do |builder|
+        builder.object do
+          builder.string "coverage"
+          builder.object do
+            hits.each do |filename, line_coverage|
+              builder.string filename
+              builder.object do
+                line_coverage.each do |line, count|
+                  builder.field line, count
+                end
+              end
+            end
+          end
+        end
+      end
+    end
   end
 
   # This visitor walks the entire reachable code tree and collect locations