rudy_dwarf/function/
variables.rs

1//! Variable resolution from DWARF debugging information
2
3use crate::{
4    die::position,
5    file::SourceLocation,
6    function::FunctionIndexEntry,
7    parser::{
8        combinators::all,
9        from_fn,
10        primitives::{entry_type, optional_attr, resolve_type_shallow},
11        Parser,
12    },
13    types::DieTypeDefinition,
14    visitor::{self, DieVisitor},
15    Die, DwarfDb,
16};
17
18type Result<T> = std::result::Result<T, crate::Error>;
19
20/// Tracked variable information
21#[derive(Debug, Clone, PartialEq, Eq, Hash, salsa::Update)]
22pub struct Variable {
23    pub name: Option<String>,
24    pub ty: DieTypeDefinition,
25    pub location: Option<SourceLocation>,
26    /// The DIE that this variable was parsed from
27    pub origin: Die,
28}
29
30impl Variable {
31    pub fn new(
32        name: Option<String>,
33        ty: DieTypeDefinition,
34        location: Option<SourceLocation>,
35        origin: Die,
36    ) -> Self {
37        Self {
38            name,
39            ty,
40            location,
41            origin,
42        }
43    }
44}
45
46/// Resolved variables for a function
47#[derive(Default, Debug, Clone, PartialEq, Eq, Hash, salsa::Update)]
48pub struct ResolvedVariables {
49    pub params: Vec<Variable>,
50    pub locals: Vec<Variable>,
51}
52
53struct VariableVisitor {
54    params: Vec<Variable>,
55    locals: Vec<Variable>,
56}
57
58impl<'db> DieVisitor<'db> for VariableVisitor {
59    fn visit_variable<'a>(
60        walker: &mut crate::visitor::DieWalker<'a, 'db, Self>,
61        node: crate::visitor::VisitorNode<'a>,
62    ) -> anyhow::Result<()> {
63        let entry = walker.get_die(node.die);
64        let db = walker.db;
65        tracing::debug!("variable: {}", entry.print(db));
66
67        match variable().parse(db, entry) {
68            Ok(var) => {
69                walker.visitor.locals.push(var);
70            }
71            Err(e) => {
72                tracing::warn!("Failed to resolve variable: {e} in {}", entry.location(db));
73            }
74        }
75        Ok(())
76    }
77
78    fn visit_parameter<'a>(
79        walker: &mut crate::visitor::DieWalker<'a, 'db, Self>,
80        node: crate::visitor::VisitorNode<'a>,
81    ) -> anyhow::Result<()> {
82        let entry = walker.get_die(node.die);
83        let db = walker.db;
84        tracing::debug!("param: {}", entry.print(db));
85        match variable().parse(db, entry) {
86            Ok(var) => {
87                walker.visitor.params.push(var);
88            }
89            Err(e) => {
90                tracing::warn!("Failed to resolve parameter: {e} in {}", entry.location(db));
91            }
92        }
93        Ok(())
94    }
95}
96
97/// Resolve all variables in a function
98///
99/// TODO(Sam): I think I should move globals outside of this
100/// since those generally happen at the top level and don't
101/// necessarily appear in this function's DIE.
102#[salsa::tracked]
103pub fn resolve_function_variables<'db>(
104    db: &'db dyn DwarfDb,
105    fie: FunctionIndexEntry<'db>,
106) -> Result<ResolvedVariables> {
107    let data = fie.data(db);
108    let die = data.specification_die.unwrap_or(data.declaration_die);
109
110    tracing::debug!(
111        "{}",
112        die.format_with_location(db, "resolving function variables")
113    );
114
115    let mut visitor = VariableVisitor {
116        params: vec![],
117        locals: vec![],
118    };
119    visitor::walk_die(db, die, &mut visitor)?;
120    Ok(ResolvedVariables {
121        params: visitor.params,
122        locals: visitor.locals,
123    })
124}
125
126pub fn variable() -> impl Parser<Variable> {
127    all((
128        optional_attr(gimli::DW_AT_name),
129        entry_type().then(resolve_type_shallow()),
130        from_fn(position),
131    ))
132    .map_with_entry(|_db, entry, (name, ty, position)| Variable::new(name, ty, position, entry))
133}