1#![warn(missing_docs)]
2
3use nargo_types::{Result, Span};
4use serde::{Deserialize, Serialize};
5use std::collections::HashMap;
6
7#[derive(Debug, Clone, Serialize, Deserialize, Default)]
9pub struct CoverageMetadata {
10 pub file_path: String,
12 pub statement_map: HashMap<usize, Span>,
14 pub branch_map: HashMap<usize, BranchMetadata>,
16 pub function_map: HashMap<usize, FunctionMetadata>,
18 pub s: HashMap<usize, u32>,
20 pub b: HashMap<usize, Vec<u32>>,
22 pub f: HashMap<usize, u32>,
24}
25
26#[derive(Debug, Clone, Serialize, Deserialize)]
28pub struct BranchMetadata {
29 pub line: usize,
31 pub r#type: String,
33 pub locations: Vec<Span>,
35}
36
37#[derive(Debug, Clone, Serialize, Deserialize)]
39pub struct FunctionMetadata {
40 pub name: String,
42 pub line: usize,
44 pub span: Span,
46}
47
48pub struct CoverageInstrumenter {
50 file_id: String,
51 next_stmt_id: usize,
52 metadata: CoverageMetadata,
53}
54
55impl CoverageInstrumenter {
56 pub fn new(file_id: String, file_path: String) -> Self {
58 Self { file_id, next_stmt_id: 0, metadata: CoverageMetadata { file_path, ..Default::default() } }
59 }
60
61 pub fn instrument(&mut self, _module: &mut ()) -> Result<CoverageMetadata> {
63 Ok(self.metadata.clone())
65 }
66
67 fn instrument_program(&mut self, _program: &mut ()) {
68 }
70
71 fn instrument_stmt(&mut self, _stmt: ()) -> () {
72 }
74
75 fn instrument_expr(&mut self, _expr: &mut ()) {
76 }
78
79 fn instrument_template(&mut self, _template: &mut ()) {
80 }
82
83 fn instrument_template_node(&mut self, _node: &mut ()) {
84 }
86
87 fn next_id(&mut self) -> usize {
88 let id = self.next_stmt_id;
89 self.next_stmt_id += 1;
90 id
91 }
92
93 fn create_counter_stmt(&self, _id: usize) -> () {
94 }
96
97 fn create_branch_counter_expr(&self, _bid: usize, _index: usize) -> () {
98 }
100
101 fn create_function_counter_stmt(&self, _fid: usize) -> () {
102 }
104}
105
106pub struct LcovReporter;
108
109impl LcovReporter {
110 pub fn report(metadatas: &[CoverageMetadata]) -> String {
112 let mut out = String::new();
113 for meta in metadatas {
114 out.push_str(&format!("TN:\nSF:{}\n", meta.file_path));
115
116 for (id, func) in &meta.function_map {
117 let hits = meta.f.get(id).cloned().unwrap_or(0);
118 out.push_str(&format!("FN:{},{}\n", func.line, func.name));
119 out.push_str(&format!("FNDA:{},{}\n", hits, func.name));
120 }
121 out.push_str(&format!("FNF:{}\n", meta.function_map.len()));
122 out.push_str(&format!("FNH:{}\n", meta.f.values().filter(|&&h| h > 0).count()));
123
124 let mut line_hits: HashMap<u32, u32> = HashMap::new();
125 for (id, span) in &meta.statement_map {
126 let hits = meta.s.get(id).cloned().unwrap_or(0);
127 let line = span.start.line;
128 let entry = line_hits.entry(line).or_insert(0);
129 *entry = (*entry).max(hits);
130 }
131
132 let mut lines: Vec<_> = line_hits.keys().collect();
133 lines.sort();
134 for &line in lines {
135 let hits = line_hits[&line];
136 out.push_str(&format!("DA:{},{}\n", line, hits));
137 }
138
139 out.push_str(&format!("LF:{}\n", line_hits.len()));
140 out.push_str(&format!("LH:{}\n", line_hits.values().filter(|&&h| h > 0).count()));
141
142 let mut branch_count = 0;
143 let mut branch_hit = 0;
144 for (bid, branch) in &meta.branch_map {
145 let hits = meta.b.get(bid);
146 for (idx, &h) in hits.map(|v| v.as_slice()).unwrap_or(&[]).iter().enumerate() {
147 out.push_str(&format!("BRDA:{},0,{},{}\n", branch.line, idx, if h > 0 { h.to_string() } else { "-".to_string() }));
148 branch_count += 1;
149 if h > 0 {
150 branch_hit += 1;
151 }
152 }
153 }
154 out.push_str(&format!("BRF:{}\n", branch_count));
155 out.push_str(&format!("BRH:{}\n", branch_hit));
156
157 out.push_str("end_of_record\n");
158 }
159 out
160 }
161}