Skip to main content

gobject_linter/
type_alias_map.rs

1use std::collections::{HashMap, HashSet};
2
3use gobject_ast::model::Project;
4
5/// Bidirectional mapping between typedef names and struct/union tags, plus
6/// GObject-synthesised aliases (`Foo` ↔ `_Foo`).
7pub struct TypeAliasMap {
8    typedef_to_tag: HashMap<String, String>,
9    tag_to_typedef: HashMap<String, String>,
10}
11
12impl TypeAliasMap {
13    pub fn build(project: &Project) -> Self {
14        let mut typedef_to_tag: HashMap<String, String> = HashMap::new();
15        let mut tag_to_typedef: HashMap<String, String> = HashMap::new();
16
17        for (_path, file) in project.files.iter().map(|(p, f)| (p.as_path(), f)) {
18            for (name, target) in file.iter_typedef_pairs() {
19                typedef_to_tag.insert(name.to_owned(), target.base_type.clone());
20                if target.is_struct || target.is_union {
21                    tag_to_typedef.insert(target.base_type.clone(), name.to_owned());
22                }
23            }
24            // G_DECLARE_FINAL_TYPE etc. expand to `typedef struct _Foo Foo` at
25            // compile time.
26            for gt in file.iter_all_gobject_types() {
27                let tag = format!("_{}", gt.type_name);
28                typedef_to_tag
29                    .entry(gt.type_name.clone())
30                    .or_insert_with(|| tag.clone());
31                tag_to_typedef
32                    .entry(tag)
33                    .or_insert_with(|| gt.type_name.clone());
34            }
35        }
36
37        Self {
38            typedef_to_tag,
39            tag_to_typedef,
40        }
41    }
42
43    /// Resolve a typedef name to its underlying struct tag, or return the name
44    /// unchanged if it is already a tag or has no known alias.
45    pub fn canonical<'a>(&'a self, name: &'a str) -> &'a str {
46        self.typedef_to_tag
47            .get(name)
48            .map_or(name, std::string::String::as_str)
49    }
50
51    /// Return the typedef alias for a struct tag, if one exists.
52    pub fn typedef_for_tag<'a>(&'a self, tag: &'a str) -> Option<&'a str> {
53        self.tag_to_typedef
54            .get(tag)
55            .map(std::string::String::as_str)
56    }
57
58    /// Return the struct tag for a typedef name, if one exists.
59    pub fn tag_for_typedef<'a>(&'a self, name: &'a str) -> Option<&'a str> {
60        self.typedef_to_tag
61            .get(name)
62            .map(std::string::String::as_str)
63    }
64
65    /// True if `name` or any of its aliases appears in `refs`.
66    pub fn is_referenced(&self, name: &str, refs: &HashSet<String>) -> bool {
67        refs.contains(name)
68            || self
69                .typedef_to_tag
70                .get(name)
71                .is_some_and(|t| refs.contains(t))
72            || self
73                .tag_to_typedef
74                .get(name)
75                .is_some_and(|a| refs.contains(a))
76    }
77
78    /// True if `(struct_name, field_name)` or any typedef/tag alias of
79    /// `struct_name` paired with `field_name` appears in `qualified`.
80    pub fn field_is_referenced(
81        &self,
82        struct_name: &str,
83        field_name: &str,
84        qualified: &HashMap<String, HashSet<String>>,
85    ) -> bool {
86        let has = |s: &str| {
87            qualified
88                .get(s)
89                .is_some_and(|fields| fields.contains(field_name))
90        };
91        has(struct_name)
92            || self.typedef_to_tag.get(struct_name).is_some_and(|t| has(t))
93            || self.tag_to_typedef.get(struct_name).is_some_and(|a| has(a))
94    }
95
96    /// Insert `(type_name, field_name)` into `qualified` under every alias of
97    /// `type_name` so that lookups via either the tag or the typedef succeed.
98    pub fn insert_qualified(
99        &self,
100        type_name: &str,
101        field_name: &str,
102        qualified: &mut HashMap<String, HashSet<String>>,
103    ) {
104        qualified
105            .entry(type_name.to_owned())
106            .or_default()
107            .insert(field_name.to_owned());
108        if let Some(alias) = self.tag_to_typedef.get(type_name) {
109            qualified
110                .entry(alias.clone())
111                .or_default()
112                .insert(field_name.to_owned());
113        }
114        if let Some(tag) = self.typedef_to_tag.get(type_name) {
115            qualified
116                .entry(tag.clone())
117                .or_default()
118                .insert(field_name.to_owned());
119        }
120    }
121}