Skip to main content

nichlink/registry_core/mir/
text.rs

1//! Textual MIR parser for `rustc -Zunpretty=mir` output.
2//! `rustc -Zunpretty=mir` 输出的文本 MIR 解析器。
3
4use super::model::{MirCall, MirGraph, MirLocal};
5
6impl MirGraph {
7    /// Parse textual MIR emitted by rustc -Zunpretty=mir.
8    /// 解析 rustc -Zunpretty=mir 输出的文本。
9    ///
10    /// Only function, local, and direct-call records are kept. Runtime
11    /// traces remain authoritative for calls that actually executed.
12    /// 这里只保留函数、局部变量和直接调用;真实执行的调用仍以运行期追踪为准。
13    ///
14    /// This never fails and never reports: text that is not MIR yields an empty
15    /// graph, and a graph with no functions is therefore the same answer for "there
16    /// is nothing here" and "this is not MIR at all". Callers that must tell those
17    /// apart check the input themselves — the command that produces MIR already
18    /// knows whether `cargo rustc` succeeded — and this is the documented boundary
19    /// rather than a defect to be discovered later.
20    /// 本函数从不失败也从不报告:不是 MIR 的文本会得到一个空图,因此"这里什么都没有"与"这根本
21    /// 不是 MIR"给出同一个答案。必须区分两者的调用方自己检查输入——产出 MIR 的那条命令本来
22    /// 就知道 `cargo rustc` 是否成功——这是**写明的**边界,而不是留给以后发现的缺陷。
23    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    /// Text that is not MIR yields an empty graph — the documented contract, not an
94    /// error. Pinned so that turning this into a `Result` has to update the test
95    /// and the doc comment together.
96    /// 不是 MIR 的文本得到空图——这是写明的契约,不是错误。钉住它,使把它改成 `Result` 的人
97    /// 必须连同这条测试与那段文档注释一起更新。
98    #[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}