nichlink/registry_core/mir/
text.rs1use super::model::{MirCall, MirGraph, MirLocal};
5
6impl MirGraph {
7 pub fn from_mir_text(input: &str) -> Self {
24 let mut graph = Self::default();
25 let mut function = String::new();
26 for raw in input.lines() {
27 let trimmed = raw.trim();
28 if let Some(name) = mir_function_name(trimmed) {
29 function = name.to_owned();
30 graph.functions.insert(function.clone());
31 continue;
32 }
33 if function.is_empty() {
34 continue;
35 }
36 if let Some(local) = mir_local(trimmed, &function, graph.locals.len() + 1) {
37 graph.locals.push(local);
38 }
39 if let Some(callee) = mir_call(trimmed) {
40 graph.calls.push(MirCall {
41 caller: function.clone(),
42 callee,
43 mir_line: graph.calls.len() + 1,
44 });
45 }
46 }
47 graph
48 }
49}
50
51fn mir_function_name(line: &str) -> Option<&str> {
52 let line = line.strip_prefix("fn ")?;
53 let end = line.find('(')?;
54 let name = line[..end].trim();
55 (!name.is_empty()).then_some(name)
56}
57
58fn mir_local(line: &str, function: &str, mir_line: usize) -> Option<MirLocal> {
59 let line = line.strip_prefix("let ")?;
60 let line = line.strip_prefix("mut ").unwrap_or(line);
61 let (name, type_name) = line.split_once(':')?;
62 let name = name.trim();
63 let type_name = type_name.trim().trim_end_matches(';').trim();
64 (name.starts_with('_') && !type_name.is_empty()).then(|| MirLocal {
65 function: function.to_owned(),
66 name: name.to_owned(),
67 type_name: type_name.to_owned(),
68 mir_line,
69 })
70}
71
72fn mir_call(line: &str) -> Option<String> {
73 let (_, expression) = line.split_once(" = ")?;
74 let open = expression.find('(')?;
75 let callee = expression[..open].trim().trim_start_matches("move ");
76 if callee.is_empty()
77 || callee.starts_with("if ")
78 || callee.starts_with("match ")
79 || matches!(
80 callee,
81 "assert" | "debug_assert" | "drop" | "panic" | "format" | "Some" | "Ok" | "Err"
82 )
83 {
84 return None;
85 }
86 Some(callee.to_owned())
87}
88
89#[cfg(test)]
90mod tests {
91 use super::MirGraph;
92
93 #[test]
99 fn text_that_is_not_mir_yields_an_empty_graph() {
100 let graph = MirGraph::from_mir_text("this is not MIR at all\n");
101 assert!(graph.functions.is_empty());
102 assert!(graph.locals.is_empty());
103 }
104
105 #[test]
106 fn parses_native_textual_mir() {
107 let graph = MirGraph::from_mir_text(
108 "fn crate::outer(_1: f32) -> f32 {\n let mut _2: f32;\n _2 = crate::inner(move _1);\n return;\n}\n",
109 );
110 assert!(graph.functions.contains("crate::outer"));
111 assert_eq!(graph.calls[0].callee, "crate::inner");
112 assert_eq!(graph.locals[0].name, "_2");
113 }
114}