tldr_cli/commands/bugbot/parsers/
cppcheck.rs1use std::path::PathBuf;
7
8use super::super::tools::{L1Finding, ToolCategory};
9use super::ParseError;
10
11pub fn parse_cppcheck_output(stdout: &str) -> Result<Vec<L1Finding>, ParseError> {
12 let mut findings = Vec::new();
13
14 for line in stdout.lines() {
15 let line = line.trim();
16 if line.is_empty() {
17 continue;
18 }
19
20 let parts: Vec<&str> = line.splitn(6, '\t').collect();
21 if parts.len() < 6 {
22 continue; }
24
25 let file = parts[0];
26 let line_num: u32 = parts[1].parse().unwrap_or(0);
27 let column: u32 = parts[2].parse().unwrap_or(0);
28 let native_sev = parts[3];
29 let id = parts[4];
30 let message = parts[5];
31
32 if native_sev == "information" {
34 continue;
35 }
36
37 let severity = match native_sev {
38 "error" => "high",
39 "warning" => "medium",
40 "style" | "performance" | "portability" => "low",
41 _ => "info",
42 };
43
44 findings.push(L1Finding {
45 tool: String::new(),
46 category: ToolCategory::Linter,
47 file: PathBuf::from(file),
48 line: line_num,
49 column,
50 native_severity: native_sev.to_string(),
51 severity: severity.to_string(),
52 message: message.to_string(),
53 code: if id.is_empty() {
54 None
55 } else {
56 Some(id.to_string())
57 },
58 });
59 }
60
61 Ok(findings)
62}
63
64#[cfg(test)]
65mod tests {
66 use super::*;
67
68 #[test]
69 fn test_parse_empty() {
70 assert!(parse_cppcheck_output("").unwrap().is_empty());
71 }
72
73 #[test]
74 fn test_parse_finding() {
75 let output = "main.c\t10\t5\twarning\tunreadVariable\tVariable 'x' is assigned a value that is never used.";
76 let findings = parse_cppcheck_output(output).unwrap();
77 assert_eq!(findings.len(), 1);
78 assert_eq!(findings[0].file, PathBuf::from("main.c"));
79 assert_eq!(findings[0].line, 10);
80 assert_eq!(findings[0].column, 5);
81 assert_eq!(findings[0].severity, "medium");
82 assert_eq!(
83 findings[0].code,
84 Some("unreadVariable".to_string())
85 );
86 }
87
88 #[test]
89 fn test_parse_skips_information() {
90 let output = "main.c\t0\t0\tinformation\tmissingInclude\tInclude file not found";
91 assert!(parse_cppcheck_output(output).unwrap().is_empty());
92 }
93
94 #[test]
95 fn test_parse_skips_malformed() {
96 let output = "Checking main.c ...\nmain.c\t5\t1\terror\tnullPointer\tNull pointer dereference";
97 let findings = parse_cppcheck_output(output).unwrap();
98 assert_eq!(findings.len(), 1);
99 assert_eq!(findings[0].severity, "high");
100 }
101}