libgir/analysis/
types.rs

1use crate::library::*;
2
3/// Array size limit above which Rust no longer automatically derives traits.
4const RUST_DERIVE_ARRAY_SIZE_LIMIT: u16 = 32;
5/// Number of parameters above which Rust no longer automatically derives traits
6/// in functions.
7const RUST_DERIVE_PARAM_SIZE_LIMIT: usize = 12;
8
9/// Checks if given type is some kind of pointer.
10pub trait IsPtr {
11    fn is_ptr(&self) -> bool;
12}
13
14impl IsPtr for Field {
15    fn is_ptr(&self) -> bool {
16        if let Some(ref c_type) = self.c_type {
17            c_type.contains('*')
18        } else {
19            // After library post processing phase
20            // only types without c:type should be
21            // function pointers, we need check their parameters.
22            false
23        }
24    }
25}
26
27impl IsPtr for Alias {
28    fn is_ptr(&self) -> bool {
29        self.target_c_type.contains('*')
30    }
31}
32
33/// Checks if given type has volatile qualifier.
34pub trait IsVolatile {
35    fn is_volatile(&self) -> bool;
36}
37
38impl IsVolatile for Field {
39    fn is_volatile(&self) -> bool {
40        if let Some(ref c_type) = self.c_type {
41            c_type.starts_with("volatile")
42        } else {
43            false
44        }
45    }
46}
47
48/// Checks if given type is incomplete, i.e., its size is unknown.
49pub trait IsIncomplete {
50    fn is_incomplete(&self, lib: &Library) -> bool;
51}
52
53impl IsIncomplete for Basic {
54    fn is_incomplete(&self, _lib: &Library) -> bool {
55        matches!(*self, Self::None | Self::Unsupported | Self::VarArgs)
56    }
57}
58
59impl IsIncomplete for Alias {
60    fn is_incomplete(&self, lib: &Library) -> bool {
61        if self.is_ptr() {
62            false
63        } else {
64            lib.type_(self.typ).is_incomplete(lib)
65        }
66    }
67}
68
69impl IsIncomplete for Field {
70    fn is_incomplete(&self, lib: &Library) -> bool {
71        if self.is_ptr() {
72            // Pointers are always complete.
73            false
74        } else {
75            lib.type_(self.typ).is_incomplete(lib)
76        }
77    }
78}
79
80impl IsIncomplete for &[Field] {
81    fn is_incomplete(&self, lib: &Library) -> bool {
82        if self.is_empty() {
83            return true;
84        }
85
86        let mut is_bitfield = false;
87        for field in self.iter() {
88            if field.is_incomplete(lib) {
89                return true;
90            }
91            // Two consequitive bitfields are unrepresentable in Rust,
92            // so from our perspective they are incomplete.
93            if is_bitfield && field.bits.is_some() {
94                return true;
95            }
96            is_bitfield = field.bits.is_some();
97        }
98
99        false
100    }
101}
102
103impl IsIncomplete for Class {
104    fn is_incomplete(&self, lib: &Library) -> bool {
105        self.fields.as_slice().is_incomplete(lib)
106    }
107}
108
109impl IsIncomplete for Record {
110    fn is_incomplete(&self, lib: &Library) -> bool {
111        if self.c_type == "GHookList" || self.disguised || self.pointer {
112            // Search for GHookList in sys codegen for rationale.
113            false
114        } else {
115            self.fields.as_slice().is_incomplete(lib)
116        }
117    }
118}
119
120impl IsIncomplete for Union {
121    fn is_incomplete(&self, lib: &Library) -> bool {
122        self.fields.as_slice().is_incomplete(lib)
123    }
124}
125
126impl IsIncomplete for Function {
127    fn is_incomplete(&self, lib: &Library) -> bool {
128        // Checking p.typ.is_incomplete(lib) cause recursive check on GScannerMsgFunc
129        self.parameters.iter().any(|p| {
130            matches!(
131                lib.type_(p.typ),
132                Type::Basic(Basic::Unsupported | Basic::VarArgs)
133            )
134        })
135    }
136}
137
138impl IsIncomplete for TypeId {
139    fn is_incomplete(&self, lib: &Library) -> bool {
140        lib.type_(*self).is_incomplete(lib)
141    }
142}
143
144impl IsIncomplete for Type {
145    fn is_incomplete(&self, lib: &Library) -> bool {
146        match self {
147            Type::Basic(basic) => basic.is_incomplete(lib),
148            Type::Alias(alias) => alias.is_incomplete(lib),
149            Type::FixedArray(tid, ..) => tid.is_incomplete(lib),
150            Type::Class(klass) => klass.is_incomplete(lib),
151            Type::Record(record) => record.is_incomplete(lib),
152            Type::Union(union) => union.is_incomplete(lib),
153            Type::Function(function) => function.is_incomplete(lib),
154            Type::Interface(..) => true,
155            Type::Custom(..)
156            | Type::Enumeration(..)
157            | Type::Bitfield(..)
158            | Type::Array(..)
159            | Type::CArray(..)
160            | Type::PtrArray(..)
161            | Type::HashTable(..)
162            | Type::List(..)
163            | Type::SList(..) => false,
164        }
165    }
166}
167
168/// Checks if type is external aka opaque type.
169pub trait IsExternal {
170    fn is_external(&self, lib: &Library) -> bool;
171}
172
173impl IsExternal for Class {
174    fn is_external(&self, _lib: &Library) -> bool {
175        self.fields.is_empty()
176    }
177}
178
179impl IsExternal for Record {
180    fn is_external(&self, _lib: &Library) -> bool {
181        self.fields.is_empty()
182    }
183}
184
185impl IsExternal for Union {
186    fn is_external(&self, _lib: &Library) -> bool {
187        self.fields.is_empty()
188    }
189}
190
191impl IsExternal for Alias {
192    fn is_external(&self, lib: &Library) -> bool {
193        if self.is_ptr() {
194            false
195        } else {
196            lib.type_(self.typ).is_external(lib)
197        }
198    }
199}
200
201impl IsExternal for Type {
202    fn is_external(&self, lib: &Library) -> bool {
203        match self {
204            Type::Alias(alias) => alias.is_external(lib),
205            Type::Class(klass) => klass.is_external(lib),
206            Type::Record(record) => record.is_external(lib),
207            Type::Union(union) => union.is_external(lib),
208            Type::Interface(..) => true,
209            Type::Custom(..)
210            | Type::Basic(..)
211            | Type::Enumeration(..)
212            | Type::Bitfield(..)
213            | Type::Function(..)
214            | Type::Array(..)
215            | Type::CArray(..)
216            | Type::FixedArray(..)
217            | Type::PtrArray(..)
218            | Type::HashTable(..)
219            | Type::List(..)
220            | Type::SList(..) => false,
221        }
222    }
223}
224
225/// Checks if given type derives Copy trait.
226pub trait DerivesCopy {
227    fn derives_copy(&self, lib: &Library) -> bool;
228}
229
230impl<T: IsIncomplete> DerivesCopy for T {
231    fn derives_copy(&self, lib: &Library) -> bool {
232        // Copy is derived for all complete types.
233        !self.is_incomplete(lib)
234    }
235}
236
237/// Checks if given type implements Debug trait.
238pub trait ImplementsDebug {
239    fn implements_debug(&self, lib: &Library) -> bool;
240}
241
242impl ImplementsDebug for Field {
243    fn implements_debug(&self, lib: &Library) -> bool {
244        match *lib.type_(self.typ) {
245            Type::FixedArray(_, size, _) => size <= RUST_DERIVE_ARRAY_SIZE_LIMIT,
246            Type::Function(Function { ref parameters, .. }) => {
247                parameters.len() <= RUST_DERIVE_PARAM_SIZE_LIMIT
248            }
249            _ => true,
250        }
251    }
252}