1use std::{
2 collections::{HashMap, HashSet},
3 path::{Path, PathBuf},
4 sync::Arc,
5};
6
7use heck::ToUpperCamelCase;
8use serde::Serialize;
9
10use crate::model::{
11 Comment, DeclareKind, DefineKind, DefineValue, EnumInfo, EnumValueDoc, Expression,
12 FunctionDeclItem, FunctionDefItem, FunctionDoc, GObjectType, GObjectTypeKind, GType,
13 InterfaceImplementation, Parameter, PreprocessorDirective, SourceLocation, Statement,
14 TopLevelItem, TypeDefItem, TypeDoc, TypeInfo, TypedefTarget, UnaryOp, VariableDecl,
15};
16
17#[derive(Debug, Clone, Serialize, Default)]
19pub struct Project {
20 #[serde(skip_serializing_if = "HashMap::is_empty")]
21 pub files: HashMap<PathBuf, FileModel>,
22}
23
24impl Project {
25 pub fn new() -> Self {
26 Self {
27 files: HashMap::new(),
28 }
29 }
30
31 pub fn resolve_all_gobject_types(&mut self) {
34 let defines: HashMap<String, DefineValue> = self
35 .files
36 .values()
37 .flat_map(FileModel::iter_defines)
38 .filter_map(|(name, value)| Some((name.to_owned(), value?.clone())))
39 .collect();
40 for file in self.files.values_mut() {
41 file.resolve_gobject_types(&defines);
42 }
43 }
44
45 pub fn get_file(&self, path: &PathBuf) -> Option<&FileModel> {
47 self.files.get(path)
48 }
49
50 pub fn is_function_declared_in_header(&self, name: &str) -> bool {
52 for file in self.files.values() {
53 if file.path.extension().is_some_and(|ext| ext == "h")
54 && file
55 .iter_function_declarations()
56 .any(|decl| decl.name == name)
57 {
58 return true;
59 }
60 }
61 false
62 }
63
64 pub fn iter_all_files(&self) -> impl Iterator<Item = (&Path, &FileModel)> {
65 self.files.iter().map(|(path, file)| (path.as_path(), file))
66 }
67
68 pub fn iter_c_files(&self) -> impl Iterator<Item = (&Path, &FileModel)> {
69 self.files
70 .iter()
71 .filter(|(path, _)| path.extension().is_some_and(|ext| ext == "c"))
72 .map(|(path, file)| (path.as_path(), file))
73 }
74
75 pub fn iter_header_files(&self) -> impl Iterator<Item = (&Path, &FileModel)> {
76 self.files
77 .iter()
78 .filter(|(path, _)| path.extension().is_some_and(|ext| ext == "h"))
79 .map(|(path, file)| (path.as_path(), file))
80 }
81
82 pub fn find_func_doc(&self, name: &str) -> Option<&FunctionDoc> {
83 self.iter_c_files()
84 .flat_map(|(_, f)| f.iter_function_definitions())
85 .find(|f| f.name == name)
86 .and_then(|f| f.doc.as_ref())
87 .or_else(|| {
88 self.iter_all_files()
89 .flat_map(|(_, f)| f.iter_function_declarations())
90 .find(|f| f.name == name)
91 .and_then(|f| f.doc.as_ref())
92 })
93 }
94
95 pub fn find_type_doc(&self, type_name: &str) -> Option<&TypeDoc> {
96 self.iter_c_files()
97 .flat_map(|(_, f)| f.iter_all_gobject_types())
98 .find(|gt| gt.type_name == type_name)
99 .and_then(|gt| gt.doc.as_ref())
100 .or_else(|| {
101 self.iter_header_files()
102 .flat_map(|(_, f)| f.iter_all_gobject_types())
103 .find(|gt| gt.type_name == type_name)
104 .and_then(|gt| gt.doc.as_ref())
105 })
106 }
107
108 pub fn find_gobject_type_by_gtype(&self, gtype: >ype) -> Option<&GObjectType> {
109 let all_types: Vec<_> = self
110 .iter_all_files()
111 .flat_map(|(_, f)| f.iter_all_gobject_types())
112 .collect();
113
114 let declare = all_types
115 .iter()
116 .copied()
117 .find(|gt| gt.type_macro.as_ref() == Some(gtype))?;
118
119 all_types
120 .iter()
121 .copied()
122 .find(|gt| gt.type_name == declare.type_name && gt.kind.is_define())
123 .or(Some(declare))
124 }
125
126 pub fn find_interface_for_property<'a>(
130 &self,
131 gobject_type: &'a GObjectType,
132 property_name: &str,
133 ) -> Option<&'a GType> {
134 for iface in &gobject_type.interfaces {
135 let Some(iface_type) = self.find_gobject_type_by_gtype(&iface.interface_type) else {
136 continue;
137 };
138 if iface_type
139 .properties
140 .iter()
141 .any(|p| p.property().name == property_name)
142 {
143 return Some(&iface.interface_type);
144 }
145 }
146 None
147 }
148
149 pub fn is_function_exported(&self, name: &str) -> bool {
151 for file in self.files.values() {
152 if file
153 .iter_function_declarations()
154 .any(|decl| decl.name == name && !decl.export_macros.is_empty())
155 {
156 return true;
157 }
158 }
159 false
160 }
161}
162
163pub struct PropertyEnumContext<'a> {
166 pub gobject_type: &'a GObjectType,
167 pub class_init: &'a FunctionDefItem,
168 pub class_type_info: Option<&'a TypeInfo>,
169 pub get_property_func: Option<&'a str>,
170 pub set_property_func: Option<&'a str>,
171}
172
173#[derive(Debug, Clone, Serialize)]
175pub struct FileModel {
176 pub path: PathBuf,
177 #[serde(skip_serializing_if = "Vec::is_empty")]
179 pub top_level_items: Vec<TopLevelItem>,
180 #[serde(skip)]
183 pub source: Arc<Vec<u8>>,
184}
185
186impl FileModel {
187 pub fn new(path: PathBuf) -> Self {
188 Self {
189 path,
190 top_level_items: Vec::new(),
191 source: Arc::new(Vec::new()),
192 }
193 }
194
195 pub fn iter_all_includes(&self) -> impl Iterator<Item = (&str, bool, SourceLocation)> + '_ {
198 self.iter_items_recursive(&self.top_level_items)
199 .filter_map(|item| match item {
200 TopLevelItem::Preprocessor(PreprocessorDirective::Include {
201 path,
202 is_system,
203 location,
204 }) => Some((path.as_str(), *is_system, location.clone())),
205 _ => None,
206 })
207 }
208
209 pub fn iter_function_definitions(&self) -> impl Iterator<Item = &FunctionDefItem> + '_ {
212 self.iter_items_recursive(&self.top_level_items)
213 .filter_map(|item| match item {
214 TopLevelItem::FunctionDefinition(func) => Some(func),
215 _ => None,
216 })
217 }
218
219 pub fn iter_class_init_functions(&self) -> impl Iterator<Item = &FunctionDefItem> + '_ {
221 self.iter_function_definitions()
222 .filter(|f| f.name.ends_with("_class_init"))
223 }
224
225 pub fn iter_function_declarations(&self) -> impl Iterator<Item = &FunctionDeclItem> + '_ {
228 self.iter_items_recursive(&self.top_level_items)
229 .filter_map(|item| match item {
230 TopLevelItem::FunctionDeclaration(func) => Some(func),
231 _ => None,
232 })
233 }
234
235 pub fn iter_all_function_names(&self) -> impl Iterator<Item = &str> + '_ {
238 self.iter_items_recursive(&self.top_level_items)
239 .filter_map(|item| match item {
240 TopLevelItem::FunctionDefinition(func) => Some(func.name.as_str()),
241 TopLevelItem::FunctionDeclaration(func) => Some(func.name.as_str()),
242 _ => None,
243 })
244 }
245
246 pub fn iter_all_gobject_types(&self) -> impl Iterator<Item = &GObjectType> + '_ {
249 self.iter_items_recursive(&self.top_level_items)
250 .filter_map(|item| match item {
251 TopLevelItem::Preprocessor(PreprocessorDirective::GObjectType(gobject_type)) => {
252 Some(gobject_type.as_ref())
253 }
254 _ => None,
255 })
256 }
257
258 pub fn find_class_struct_for(&self, gobject_type: &GObjectType) -> Option<&TypeDefItem> {
262 let name = gobject_type.class_struct_name()?;
263 self.iter_class_structs().find(|td| {
264 if let TypeDefItem::Struct { name: n, .. } = td {
265 n.trim_start_matches('_') == name
266 } else {
267 false
268 }
269 })
270 }
271
272 pub fn iter_class_structs(&self) -> impl Iterator<Item = &TypeDefItem> + '_ {
275 self.iter_items_recursive(&self.top_level_items)
276 .filter_map(|item| match item {
277 TopLevelItem::TypeDefinition(td @ TypeDefItem::Struct { vfuncs, .. })
278 if !vfuncs.is_empty() =>
279 {
280 Some(td)
281 }
282 _ => None,
283 })
284 }
285
286 pub fn iter_defines(&self) -> impl Iterator<Item = (&str, Option<&DefineValue>)> + '_ {
287 self.iter_items_recursive(&self.top_level_items)
288 .filter_map(|item| match item {
289 TopLevelItem::Preprocessor(PreprocessorDirective::Define {
290 name, value, ..
291 }) => Some((name.as_str(), value.as_ref())),
292 _ => None,
293 })
294 }
295
296 pub fn iter_comments(&self) -> impl Iterator<Item = &Comment> + '_ {
299 self.iter_items_recursive(&self.top_level_items)
300 .filter_map(|item| match item {
301 TopLevelItem::Comment(c) => Some(c),
302 _ => None,
303 })
304 }
305
306 pub fn iter_all_enums(&self) -> impl Iterator<Item = &EnumInfo> + '_ {
308 self.iter_items_recursive(&self.top_level_items)
309 .filter_map(|item| match item {
310 TopLevelItem::TypeDefinition(TypeDefItem::Enum(enum_info)) => {
311 Some(enum_info.as_ref())
312 }
313 _ => None,
314 })
315 }
316
317 pub fn iter_property_enums(&self) -> impl Iterator<Item = &EnumInfo> + '_ {
321 self.iter_all_enums().filter(|e| e.is_property_enum())
322 }
323
324 pub fn find_gobject_type_for_signal_enum(&self, enum_info: &EnumInfo) -> Option<&GObjectType> {
327 let signal_names: Vec<&str> = enum_info
328 .values
329 .iter()
330 .filter(|v| !v.is_signal_last())
331 .map(|v| v.name.as_str())
332 .collect();
333
334 let n_signals_name = enum_info
335 .values
336 .last()
337 .filter(|v| v.is_signal_last())
338 .map(|v| v.name.as_str());
339
340 let arrays = self.find_typed_arrays("guint", false, n_signals_name);
341 let array_names: Vec<&str> = arrays.iter().map(|d| d.name.as_str()).collect();
342
343 self.iter_all_gobject_types().find(|gt| {
344 let class_init_name = gt.class_init_function_name();
345 let Some(func) = self
346 .iter_function_definitions()
347 .find(|f| f.name == class_init_name)
348 else {
349 return false;
350 };
351
352 if !array_names.is_empty() {
354 let uses_array = func
355 .body_statements
356 .iter()
357 .flat_map(Statement::iter_assignments)
358 .any(|a| {
359 matches!(&*a.lhs, Expression::Subscript(sub)
360 if matches!(&*sub.array, Expression::Identifier(id)
361 if array_names.contains(&id.name.as_str())))
362 });
363 if uses_array {
364 return true;
365 }
366 }
367
368 func.body_statements
371 .iter()
372 .flat_map(Statement::iter_assignments)
373 .any(|a| {
374 if let Expression::Subscript(sub) = &*a.lhs
375 && let Expression::Identifier(index_id) = &*sub.index
376 && signal_names.contains(&index_id.name.as_str())
377 && let Expression::Call(call) = &*a.rhs
378 && call.function_contains("g_signal_new")
379 {
380 true
381 } else {
382 false
383 }
384 })
385 })
386 }
387
388 pub fn find_typed_arrays(
401 &self,
402 base_type: &str,
403 is_pointer: bool,
404 sentinel_name: Option<&str>,
405 ) -> Vec<&VariableDecl> {
406 self.iter_all_items()
407 .filter_map(|item| {
408 let TopLevelItem::Declaration(decl) = item else {
409 return None;
410 };
411 if !decl.type_info.is_base_type(base_type)
412 || decl.type_info.is_pointer() != is_pointer
413 {
414 return None;
415 }
416 let matches = match &decl.array_size {
417 Some(Expression::Identifier(size_id)) => {
418 sentinel_name.is_none_or(|s| size_id.name == s)
419 }
420 Some(Expression::Binary(_)) => sentinel_name.is_none(),
421 Some(_) => sentinel_name.is_none(),
422 None => false,
423 };
424 matches.then_some(decl.as_ref())
425 })
426 .collect()
427 }
428
429 pub fn find_gobject_type_for_property_enum(
431 &self,
432 enum_info: &EnumInfo,
433 ) -> Option<&GObjectType> {
434 let property_names: Vec<&str> = enum_info
435 .values
436 .iter()
437 .filter(|v| !v.is_prop_0() && !v.is_prop_last())
438 .map(|v| v.name.as_str())
439 .collect();
440
441 let n_props_name = enum_info
442 .values
443 .last()
444 .filter(|v| v.is_prop_last())
445 .map(|v| v.name.as_str());
446
447 self.iter_all_gobject_types().find(|gt| {
448 let has_matching_property = gt.properties.iter().any(|a| {
450 a.get_installed_enum_value()
451 .is_some_and(|ev| property_names.contains(&ev))
452 });
453 if has_matching_property {
454 return true;
455 }
456
457 if let Some(sentinel) = n_props_name {
460 let class_init_name = gt.class_init_function_name();
461 if let Some(func) = self
462 .iter_function_definitions()
463 .find(|f| f.name == class_init_name)
464 {
465 let install_calls = func.find_install_properties_calls();
466 return install_calls.iter().any(|call| {
467 call.get_arg(1)
468 .and_then(|arg| arg.location().as_str())
469 .is_some_and(|name| name == sentinel)
470 });
471 }
472 }
473
474 false
475 })
476 }
477
478 pub fn resolve_class_init_vfuncs<'a>(
486 &'a self,
487 gobject_type: &GObjectType,
488 ) -> HashMap<(String, &'a str), &'a str> {
489 let class_init_name = gobject_type.class_init_function_name();
490 let Some(class_init) = self
491 .iter_function_definitions()
492 .find(|f| f.name == class_init_name)
493 else {
494 return HashMap::new();
495 };
496
497 let mut var_types: HashMap<&str, String> = HashMap::new();
498
499 if let Some(Parameter::Regular {
500 name: Some(name),
501 type_info,
502 ..
503 }) = class_init.parameters.first()
504 {
505 var_types.insert(name.as_str(), type_info.base_type.clone());
506 }
507
508 for stmt in &class_init.body_statements {
509 for decl in stmt.iter_declarations() {
510 if let Some(init) = &decl.initializer
511 && let Expression::Call(call) = init
512 {
513 let name = call.function_name();
514 if let Some(prefix) = name
515 .strip_suffix("_CLASS")
516 .or_else(|| name.strip_suffix("_GET_CLASS"))
517 {
518 var_types.insert(
519 decl.name.as_str(),
520 format!("{}Class", prefix.to_upper_camel_case()),
521 );
522 continue;
523 }
524 }
525 var_types.insert(decl.name.as_str(), decl.type_info.base_type.clone());
526 }
527 }
528
529 let func_names: HashSet<&str> = self
530 .iter_function_definitions()
531 .map(|f| f.name.as_str())
532 .collect();
533
534 let mut vfuncs = HashMap::new();
535 for assignment in class_init
536 .body_statements
537 .iter()
538 .flat_map(Statement::iter_assignments)
539 {
540 if let Expression::FieldAccess(fa) = &*assignment.lhs
541 && let Expression::Identifier(ident) = assignment.rhs.as_ref()
542 && func_names.contains(ident.name.as_str())
543 {
544 let class_type = match &*fa.base {
545 Expression::Identifier(id) => var_types.get(id.name.as_str()).cloned(),
546 Expression::Call(call) => {
547 let name = call.function_name();
548 let prefix = name
549 .strip_suffix("_CLASS")
550 .or_else(|| name.strip_suffix("_GET_CLASS"));
551 prefix.map(|p| format!("{}Class", p.to_upper_camel_case()))
552 }
553 _ => None,
554 };
555 if let Some(ct) = class_type {
556 vfuncs.insert((ct, fa.field.as_str()), ident.name.as_str());
557 }
558 }
559 }
560 vfuncs
561 }
562
563 pub fn resolve_property_enum_context(
564 &self,
565 enum_info: &EnumInfo,
566 ) -> Option<PropertyEnumContext<'_>> {
567 let gobject_type = self.find_gobject_type_for_property_enum(enum_info)?;
568 let class_init_name = gobject_type.class_init_function_name();
569 let class_init = self
570 .iter_function_definitions()
571 .find(|f| f.name == class_init_name)?;
572
573 let class_type_info = class_init.parameters.first().and_then(|p| {
574 if let Parameter::Regular { type_info, .. } = p {
575 Some(type_info)
576 } else {
577 None
578 }
579 });
580
581 let vfuncs = self.resolve_class_init_vfuncs(gobject_type);
582 let get_property_func = vfuncs
583 .iter()
584 .find(|((_, field), _)| *field == "get_property")
585 .map(|(_, func)| *func);
586 let set_property_func = vfuncs
587 .iter()
588 .find(|((_, field), _)| *field == "set_property")
589 .map(|(_, func)| *func);
590
591 Some(PropertyEnumContext {
592 gobject_type,
593 class_init,
594 class_type_info,
595 get_property_func,
596 set_property_func,
597 })
598 }
599
600 pub fn iter_all_items(&self) -> impl Iterator<Item = &TopLevelItem> + '_ {
604 self.iter_items_recursive(&self.top_level_items)
605 }
606
607 pub fn has_declarations(&self) -> bool {
608 self.iter_all_items().any(|item| {
609 matches!(
610 item,
611 TopLevelItem::TypeDefinition(_)
612 | TopLevelItem::FunctionDeclaration(_)
613 | TopLevelItem::FunctionDefinition(_)
614 | TopLevelItem::Declaration(_)
615 )
616 })
617 }
618
619 pub fn iter_typedef_pairs(&self) -> impl Iterator<Item = (&str, &TypeInfo)> + '_ {
624 self.iter_all_items().filter_map(|item| match item {
625 TopLevelItem::TypeDefinition(TypeDefItem::Typedef {
626 name,
627 target: TypedefTarget::Type(target_type),
628 struct_fields,
629 ..
630 }) if struct_fields.is_empty() && !target_type.base_type.is_empty() => {
631 Some((name.as_str(), target_type))
632 }
633 _ => None,
634 })
635 }
636
637 pub fn resolve_gobject_types(&mut self, defines: &HashMap<String, DefineValue>) {
641 self.extract_manual_declare_types();
642 Self::extract_manual_gobject_types(&mut self.top_level_items);
643 Self::resolve_items(&mut self.top_level_items, defines);
644 }
645
646 fn extract_manual_declare_types(&mut self) {
650 let existing_macros: Vec<GType> = self
651 .iter_all_items()
652 .filter_map(|item| match item {
653 TopLevelItem::Preprocessor(PreprocessorDirective::GObjectType(gt)) => {
654 gt.type_macro.clone()
655 }
656 _ => None,
657 })
658 .collect();
659
660 struct DefineInfo {
662 name: String,
663 value: DefineValue,
664 location: SourceLocation,
665 }
666 let defines: Vec<DefineInfo> = self
667 .iter_all_items()
668 .filter_map(|item| match item {
669 TopLevelItem::Preprocessor(PreprocessorDirective::Define {
670 name,
671 value: Some(value),
672 location,
673 }) => Some(DefineInfo {
674 name: name.clone(),
675 value: value.clone(),
676 location: location.clone(),
677 }),
678 _ => None,
679 })
680 .collect();
681
682 let mut new_types = Vec::new();
683
684 for define in &defines {
685 if !define.name.contains("_TYPE_") {
687 continue;
688 }
689 let gtype = GType::Identifier(define.name.clone());
690 if existing_macros.contains(>ype) {
691 continue;
692 }
693
694 let raw = define.value.as_raw_str();
696 let trimmed = raw.trim().trim_matches(|c| c == '(' || c == ')');
697 let Some(func_name) = trimmed.split_whitespace().next() else {
698 continue;
699 };
700 let Some(function_prefix) = func_name.strip_suffix("_get_type") else {
701 continue;
702 };
703
704 let Some((module_prefix, type_suffix)) = define.name.split_once("_TYPE_") else {
708 continue;
709 };
710 let cast_macro_name = format!("{}_{}", module_prefix, type_suffix);
711
712 let type_name = defines
713 .iter()
714 .find(|d| d.name == cast_macro_name)
715 .and_then(|d| {
716 d.value
720 .as_raw_str()
721 .rsplit(',')
722 .next()
723 .map(|s| s.trim().trim_end_matches(')').trim().to_owned())
724 });
725
726 let Some(type_name) = type_name else {
727 continue;
728 };
729 if type_name.is_empty() || !type_name.chars().next().unwrap().is_uppercase() {
730 continue;
731 }
732
733 let already_exists = self.iter_all_items().any(|item| {
735 matches!(
736 item,
737 TopLevelItem::Preprocessor(PreprocessorDirective::GObjectType(gt))
738 if gt.type_name == type_name
739 )
740 });
741 if already_exists {
742 continue;
743 }
744
745 new_types.push(TopLevelItem::Preprocessor(
746 PreprocessorDirective::GObjectType(Box::new(GObjectType {
747 type_name,
748 type_macro: Some(gtype),
749 function_prefix: function_prefix.to_owned(),
750 parent_type: None,
751 flags: None,
752 kind: GObjectTypeKind::Declare {
753 kind: DeclareKind::Derivable,
754 module_prefix: module_prefix.to_owned(),
755 type_prefix: type_suffix.to_owned(),
756 },
757 interfaces: Vec::new(),
758 has_private: false,
759 manually_registered: true,
760 code_block_statements: Vec::new(),
761 export_macros: Vec::new(),
762 doc: None,
763 properties: Vec::new(),
764 signals: Vec::new(),
765 location: define.location.clone(),
766 })),
767 ));
768 }
769
770 self.top_level_items.extend(new_types);
771 }
772
773 fn extract_manual_gobject_types(items: &mut Vec<TopLevelItem>) {
778 let existing_type_names: Vec<String> = items
779 .iter()
780 .filter_map(|item| match item {
781 TopLevelItem::Preprocessor(PreprocessorDirective::GObjectType(gt)) => {
782 Some(gt.type_name.clone())
783 }
784 _ => None,
785 })
786 .collect();
787
788 let mut new_types = Vec::new();
789
790 for item in items.iter() {
791 let TopLevelItem::FunctionDefinition(func) = item else {
792 continue;
793 };
794 if !func.name.ends_with("_get_type") || func.return_type.base_type != "GType" {
795 continue;
796 }
797
798 let function_prefix = func.name.strip_suffix("_get_type").unwrap();
799
800 let register_call = func.find_calls_matching(|name| {
801 name == "g_type_register_static" || name == "g_type_register_static_simple"
802 });
803 let Some(call) = register_call.first() else {
804 continue;
805 };
806
807 let Some(parent_expr) = call.get_arg(0) else {
809 continue;
810 };
811 let parent_type = match parent_expr {
812 Expression::Identifier(id) => id.name.clone(),
813 _ => continue,
814 };
815
816 let Some(type_name) = call.extract_string_from_arg(1) else {
817 continue;
818 };
819 let type_name = type_name.trim_matches('"').to_owned();
820
821 if existing_type_names.contains(&type_name) {
822 continue;
823 }
824
825 let flags = call
826 .get_arg(if call.arguments.len() > 4 { 6 } else { 3 })
827 .and_then(|e| match e {
828 Expression::Identifier(id) => Some(id.name.as_str()),
829 _ => None,
830 });
831
832 let is_interface = parent_type == "G_TYPE_INTERFACE";
833 let is_abstract = flags.is_some_and(|f| f.contains("ABSTRACT"));
834
835 let has_private = !func.find_calls(&["g_type_add_instance_private"]).is_empty();
836
837 let kind = if is_interface {
838 GObjectTypeKind::Define(DefineKind::Interface)
839 } else if is_abstract {
840 GObjectTypeKind::Define(DefineKind::AbstractType)
841 } else {
842 GObjectTypeKind::Define(DefineKind::Type)
843 };
844
845 let iface_calls = func.find_calls(&["g_type_add_interface_static"]);
846 let mut interfaces = Vec::new();
847 for iface_call in &iface_calls {
848 let Some(iface_type_expr) = iface_call.get_arg(1) else {
849 continue;
850 };
851 let Expression::Identifier(iface_id) = iface_type_expr else {
852 continue;
853 };
854 let interface_type = GType::Identifier(iface_id.name.clone());
855
856 let init_function = iface_call
859 .get_arg(2)
860 .and_then(|e| match e {
861 Expression::Unary(u) if u.operator == UnaryOp::AddressOf => {
862 match u.operand.as_ref() {
863 Expression::Identifier(id) => Some(id.name.as_str()),
864 _ => None,
865 }
866 }
867 _ => None,
868 })
869 .and_then(|var_name| {
870 Self::find_init_func_from_iface_info(&func.body_statements, var_name)
871 });
872
873 interfaces.push(InterfaceImplementation {
874 interface_type,
875 init_function,
876 });
877 }
878
879 new_types.push(TopLevelItem::Preprocessor(
880 PreprocessorDirective::GObjectType(Box::new(GObjectType {
881 type_name,
882 type_macro: None,
883 function_prefix: function_prefix.to_owned(),
884 parent_type: Some(parent_type),
885 flags: flags.map(std::borrow::ToOwned::to_owned),
886 kind,
887 interfaces,
888 has_private,
889 manually_registered: true,
890 code_block_statements: Vec::new(),
891 export_macros: Vec::new(),
892 doc: None,
893 properties: Vec::new(),
894 signals: Vec::new(),
895 location: func.location.clone(),
896 })),
897 ));
898 }
899
900 items.extend(new_types);
901 }
902
903 fn find_init_func_from_iface_info(stmts: &[Statement], var_name: &str) -> Option<String> {
908 for stmt in stmts {
909 match stmt {
910 Statement::Declaration(decl) if decl.name == var_name => {
911 let Expression::InitializerList(init) = decl.initializer.as_ref()? else {
912 return None;
913 };
914 let first = init
915 .items
916 .iter()
917 .find(|item| !matches!(&*item.value, Expression::Comment(_)))?;
918 return Some(Self::unwrap_cast_to_identifier(&first.value)?.to_owned());
919 }
920 Statement::If(if_stmt) => {
921 if let Some(v) =
922 Self::find_init_func_from_iface_info(&if_stmt.then_body, var_name)
923 {
924 return Some(v);
925 }
926 if let Some(else_body) = &if_stmt.else_body
927 && let Some(v) = Self::find_init_func_from_iface_info(else_body, var_name)
928 {
929 return Some(v);
930 }
931 }
932 Statement::Compound(c) => {
933 if let Some(v) = Self::find_init_func_from_iface_info(&c.statements, var_name) {
934 return Some(v);
935 }
936 }
937 _ => {}
938 }
939 }
940 None
941 }
942
943 fn unwrap_cast_to_identifier(expr: &Expression) -> Option<&str> {
945 match expr {
946 Expression::Identifier(id) => Some(&id.name),
947 Expression::Cast(c) => Self::unwrap_cast_to_identifier(&c.operand),
948 _ => None,
949 }
950 }
951
952 fn resolve_items(items: &mut [TopLevelItem], defines: &HashMap<String, DefineValue>) {
953 for i in 0..items.len() {
954 match &items[i] {
955 TopLevelItem::Preprocessor(PreprocessorDirective::GObjectType(gt)) => {
956 let type_name = gt.type_name.clone();
957
958 let init_names = if gt.is_interface() {
959 vec![
960 gt.default_init_function_name(),
961 gt.class_init_function_name(),
962 ]
963 } else {
964 vec![gt.class_init_function_name()]
965 };
966
967 let func_idx = items.iter().position(|item| {
968 matches!(item, TopLevelItem::FunctionDefinition(f) if init_names.contains(&f.name))
969 });
970 if let Some(j) = func_idx {
971 let func = match &items[j] {
972 TopLevelItem::FunctionDefinition(f) => f,
973 _ => unreachable!(),
974 };
975
976 let properties = func.find_param_spec_assignments(&type_name, defines);
977 let signals = func.find_signal_registrations(&type_name);
978 if let TopLevelItem::Preprocessor(PreprocessorDirective::GObjectType(gt)) =
979 &mut items[i]
980 {
981 gt.properties = properties;
982 gt.signals = signals;
983 }
984 }
985
986 if let Some(doc) = Self::find_type_doc_in_comments(items, &type_name)
987 && let TopLevelItem::Preprocessor(PreprocessorDirective::GObjectType(gt)) =
988 &mut items[i]
989 {
990 gt.doc = Some(doc);
991 }
992 }
993 TopLevelItem::Preprocessor(PreprocessorDirective::Conditional { .. })
994 | TopLevelItem::Preprocessor(PreprocessorDirective::GObjectDeclsBlock { .. }) => {}
995 _ => {}
996 }
997 }
998
999 Self::resolve_type_docs(items);
1000
1001 for item in items.iter_mut() {
1002 match item {
1003 TopLevelItem::Preprocessor(PreprocessorDirective::Conditional { body, .. })
1004 | TopLevelItem::Preprocessor(PreprocessorDirective::GObjectDeclsBlock {
1005 body,
1006 ..
1007 }) => {
1008 Self::resolve_items(body, defines);
1009 }
1010 _ => {}
1011 }
1012 }
1013 }
1014
1015 fn find_type_doc_in_comments(items: &[TopLevelItem], type_name: &str) -> Option<TypeDoc> {
1016 items.iter().find_map(|item| {
1017 if let TopLevelItem::Comment(c) = item {
1018 let doc = TypeDoc::from_comment(c)?;
1019 let sym = doc.symbol.as_deref()?;
1020 if sym == type_name {
1021 return Some(doc);
1022 }
1023 }
1024 None
1025 })
1026 }
1027
1028 fn resolve_type_docs(items: &mut [TopLevelItem]) {
1029 for i in 0..items.len() {
1030 let name = match &items[i] {
1031 TopLevelItem::TypeDefinition(
1032 TypeDefItem::Struct {
1033 name, doc: None, ..
1034 }
1035 | TypeDefItem::Typedef {
1036 name, doc: None, ..
1037 },
1038 ) => Some(name.trim_start_matches('_').to_owned()),
1039 TopLevelItem::TypeDefinition(TypeDefItem::Enum(e)) if e.doc.is_none() => {
1040 e.name.clone()
1041 }
1042 _ => None,
1043 };
1044 if let Some(name) = name
1045 && let Some(doc) = Self::find_type_doc_in_comments(items, &name)
1046 {
1047 match &mut items[i] {
1048 TopLevelItem::TypeDefinition(
1049 TypeDefItem::Struct { doc: d, .. } | TypeDefItem::Typedef { doc: d, .. },
1050 ) => {
1051 *d = Some(doc);
1052 }
1053 TopLevelItem::TypeDefinition(TypeDefItem::Enum(e)) => {
1054 e.doc = Some(doc);
1055 }
1056 _ => {}
1057 }
1058 }
1059 }
1060
1061 Self::resolve_enum_value_docs(items);
1062 }
1063
1064 fn resolve_enum_value_docs(items: &mut [TopLevelItem]) {
1065 for i in 0..items.len() {
1066 let TopLevelItem::TypeDefinition(TypeDefItem::Enum(e)) = &mut items[i] else {
1067 continue;
1068 };
1069
1070 let enum_name = match &e.name {
1072 Some(n) => n.clone(),
1073 None => continue,
1074 };
1075 let inline_docs: Vec<(String, EnumValueDoc)> = items
1076 .iter()
1077 .find_map(|item| {
1078 if let TopLevelItem::Comment(c) = item {
1079 let doc = TypeDoc::from_comment(c)?;
1080 if doc.symbol.as_deref() == Some(&enum_name) {
1081 return Some(EnumValueDoc::extract_inline_from_comment(c));
1082 }
1083 }
1084 None
1085 })
1086 .unwrap_or_default();
1087
1088 let TopLevelItem::TypeDefinition(TypeDefItem::Enum(e)) = &mut items[i] else {
1090 continue;
1091 };
1092
1093 for value in &mut e.values {
1094 if let Some((_, doc)) = inline_docs.iter().find(|(n, _)| *n == value.name) {
1096 value.doc = Some(doc.clone());
1097 }
1098 }
1099
1100 let missing: Vec<String> = e
1102 .values
1103 .iter()
1104 .filter(|v| v.doc.is_none())
1105 .map(|v| v.name.clone())
1106 .collect();
1107
1108 let standalone_docs: Vec<(String, EnumValueDoc)> = missing
1109 .iter()
1110 .filter_map(|name| {
1111 let doc = items.iter().find_map(|item| {
1112 if let TopLevelItem::Comment(c) = item {
1113 let doc = EnumValueDoc::from_comment(c)?;
1114 if doc.symbol.as_deref() == Some(name.as_str()) {
1115 return Some(doc);
1116 }
1117 }
1118 None
1119 })?;
1120 Some((name.clone(), doc))
1121 })
1122 .collect();
1123
1124 let TopLevelItem::TypeDefinition(TypeDefItem::Enum(e)) = &mut items[i] else {
1125 continue;
1126 };
1127 for (name, doc) in standalone_docs {
1128 if let Some(value) = e.values.iter_mut().find(|v| v.name == name) {
1129 value.doc = Some(doc);
1130 }
1131 }
1132 }
1133 }
1134
1135 fn iter_items_recursive<'a>(
1137 &'a self,
1138 items: &'a [TopLevelItem],
1139 ) -> Box<dyn Iterator<Item = &'a TopLevelItem> + 'a> {
1140 Box::new(items.iter().flat_map(move |item| match item {
1141 TopLevelItem::Preprocessor(PreprocessorDirective::Conditional { body, .. })
1142 | TopLevelItem::Preprocessor(PreprocessorDirective::GObjectDeclsBlock {
1143 body, ..
1144 }) => Box::new(std::iter::once(item).chain(self.iter_items_recursive(body)))
1145 as Box<dyn Iterator<Item = &'a TopLevelItem>>,
1146 _ => Box::new(std::iter::once(item)) as Box<dyn Iterator<Item = &'a TopLevelItem>>,
1147 }))
1148 }
1149
1150 fn iter_items_with_parent<'a>(
1151 &'a self,
1152 items: &'a [TopLevelItem],
1153 parent: Option<&'a TopLevelItem>,
1154 ) -> Box<dyn Iterator<Item = (&'a TopLevelItem, Option<&'a TopLevelItem>)> + 'a> {
1155 Box::new(items.iter().flat_map(move |item| {
1156 match item {
1157 TopLevelItem::Preprocessor(PreprocessorDirective::Conditional { body, .. })
1158 | TopLevelItem::Preprocessor(PreprocessorDirective::GObjectDeclsBlock {
1159 body,
1160 ..
1161 }) => Box::new(
1162 std::iter::once((item, parent))
1163 .chain(self.iter_items_with_parent(body, Some(item))),
1164 )
1165 as Box<dyn Iterator<Item = (&'a TopLevelItem, Option<&'a TopLevelItem>)>>,
1166 _ => Box::new(std::iter::once((item, parent)))
1167 as Box<dyn Iterator<Item = (&'a TopLevelItem, Option<&'a TopLevelItem>)>>,
1168 }
1169 }))
1170 }
1171
1172 pub fn iter_all_items_with_parent(
1173 &self,
1174 ) -> impl Iterator<Item = (&TopLevelItem, Option<&TopLevelItem>)> + '_ {
1175 self.iter_items_with_parent(&self.top_level_items, None)
1176 }
1177}