1use std::collections::HashMap;
2
3use clap::ValueEnum;
4use serde::{Serialize, Serializer, ser::SerializeMap};
5
6use crate::{
7 Comment, EnumInfo, GObjectType, TypeInfo, VariableDecl, VirtualFunction,
8 model::{
9 SourceLocation, Statement,
10 doc::{FunctionDoc, PropertyDoc, SignalDoc, TypeDoc},
11 expression::{CallExpression, Expression},
12 types::{ParamSpecAssignment, Parameter, Property, Signal},
13 },
14};
15
16#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, ValueEnum)]
19#[serde(rename_all = "snake_case")]
20pub enum TopLevelItemKind {
21 FunctionDefinition,
22 FunctionDeclaration,
23 Typedef,
24 Struct,
25 Enum,
26 Include,
27 Define,
28 GObjectType,
29 Conditional,
30 GObjectDeclsBlock,
31 Declaration,
32 Expression,
33 Other,
34}
35
36#[derive(Debug, Clone)]
38pub enum TopLevelItem {
39 Preprocessor(PreprocessorDirective),
41 TypeDefinition(TypeDefItem),
43 FunctionDeclaration(FunctionDeclItem),
45 FunctionDefinition(FunctionDefItem),
47 Declaration(Box<VariableDecl>),
49 Expression(Box<Expression>),
51 Comment(Comment),
53}
54
55impl TopLevelItem {
56 pub fn name(&self) -> Option<&str> {
58 match self {
59 Self::FunctionDefinition(f) => Some(&f.name),
60 Self::FunctionDeclaration(f) => Some(&f.name),
61 Self::TypeDefinition(td) => match td {
62 TypeDefItem::Typedef { name, .. } | TypeDefItem::Struct { name, .. } => Some(name),
63 TypeDefItem::Enum(enum_info) => enum_info.name.as_deref(),
64 },
65 Self::Preprocessor(PreprocessorDirective::Include { path, .. }) => Some(path),
66 Self::Preprocessor(PreprocessorDirective::Define { name, .. }) => Some(name),
67 Self::Preprocessor(PreprocessorDirective::GObjectType(gobject_type)) => {
68 Some(&gobject_type.type_name)
69 }
70 Self::Declaration(decl) => Some(&decl.name),
71 Self::Comment(_) => None,
72 _ => None,
73 }
74 }
75
76 pub fn kind(&self) -> TopLevelItemKind {
78 match self {
79 Self::FunctionDefinition(_) => TopLevelItemKind::FunctionDefinition,
80 Self::FunctionDeclaration(_) => TopLevelItemKind::FunctionDeclaration,
81 Self::TypeDefinition(TypeDefItem::Typedef { .. }) => TopLevelItemKind::Typedef,
82 Self::TypeDefinition(TypeDefItem::Struct { .. }) => TopLevelItemKind::Struct,
83 Self::TypeDefinition(TypeDefItem::Enum(_)) => TopLevelItemKind::Enum,
84 Self::Preprocessor(PreprocessorDirective::Include { .. }) => TopLevelItemKind::Include,
85 Self::Preprocessor(PreprocessorDirective::Define { .. }) => TopLevelItemKind::Define,
86 Self::Preprocessor(PreprocessorDirective::GObjectType { .. }) => {
87 TopLevelItemKind::GObjectType
88 }
89 Self::Preprocessor(PreprocessorDirective::Conditional { .. }) => {
90 TopLevelItemKind::Conditional
91 }
92 Self::Preprocessor(PreprocessorDirective::GObjectDeclsBlock { .. }) => {
93 TopLevelItemKind::GObjectDeclsBlock
94 }
95 Self::Declaration(_) => TopLevelItemKind::Declaration,
96 Self::Expression(_) => TopLevelItemKind::Expression,
97 Self::Comment(_) => TopLevelItemKind::Other,
98 Self::Preprocessor(_) => TopLevelItemKind::Other,
99 }
100 }
101}
102
103impl Serialize for TopLevelItem {
104 fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
105 match self {
106 Self::Preprocessor(directive) => directive.serialize(s),
107 Self::TypeDefinition(v) => {
108 let mut m = s.serialize_map(Some(1))?;
109 m.serialize_entry("type_definition", v)?;
110 m.end()
111 }
112 Self::FunctionDeclaration(v) => {
113 let mut m = s.serialize_map(Some(1))?;
114 m.serialize_entry("function_declaration", v)?;
115 m.end()
116 }
117 Self::FunctionDefinition(v) => {
118 let mut m = s.serialize_map(Some(1))?;
119 m.serialize_entry("function_definition", v)?;
120 m.end()
121 }
122 Self::Declaration(v) => {
123 let mut m = s.serialize_map(Some(1))?;
124 m.serialize_entry("declaration", v)?;
125 m.end()
126 }
127 Self::Expression(v) => {
128 let mut m = s.serialize_map(Some(1))?;
129 m.serialize_entry("expression", v)?;
130 m.end()
131 }
132 Self::Comment(v) => {
133 let mut m = s.serialize_map(Some(1))?;
134 m.serialize_entry("comment", v)?;
135 m.end()
136 }
137 }
138 }
139}
140
141#[derive(Debug, Clone, Serialize)]
142#[serde(rename_all = "snake_case")]
143pub enum PragmaKind {
144 Once,
146 DiagnosticPush,
148 DiagnosticPop,
150 DiagnosticIgnored { warning: String },
152 Other {
154 name: String,
155 arguments: Option<String>,
156 },
157}
158
159#[derive(Debug, Clone, Serialize)]
160#[serde(rename_all = "snake_case")]
161pub enum PreprocessorDirective {
162 Include {
163 path: String,
164 is_system: bool,
165 location: SourceLocation,
166 },
167 Define {
168 name: String,
169 value: Option<String>,
170 location: SourceLocation,
171 },
172 Call {
173 directive: String,
174 location: SourceLocation,
175 },
176 Pragma {
177 kind: PragmaKind,
178 location: SourceLocation,
179 },
180 GObjectType(Box<GObjectType>),
182 AutoptrCleanupFunc {
184 type_name: String,
185 cleanup_function: String,
186 location: SourceLocation,
187 },
188 AutoCleanupClearFunc {
190 type_name: String,
191 cleanup_function: String,
192 location: SourceLocation,
193 },
194 MacroWithCode {
197 macro_name: String,
198 arguments: Vec<String>,
199 code_statements: Vec<Statement>,
200 location: SourceLocation,
201 },
202 Conditional {
203 kind: ConditionalKind,
204 condition: Option<String>,
205 body: Vec<TopLevelItem>,
206 location: SourceLocation,
207 },
208 GObjectDeclsBlock {
210 body: Vec<TopLevelItem>,
211 location: SourceLocation,
212 },
213}
214
215impl PreprocessorDirective {
216 pub fn location(&self) -> &SourceLocation {
217 match self {
218 Self::Include { location, .. }
219 | Self::Define { location, .. }
220 | Self::Call { location, .. }
221 | Self::Pragma { location, .. }
222 | Self::AutoptrCleanupFunc { location, .. }
223 | Self::AutoCleanupClearFunc { location, .. }
224 | Self::MacroWithCode { location, .. }
225 | Self::Conditional { location, .. }
226 | Self::GObjectDeclsBlock { location, .. } => location,
227 Self::GObjectType(gt) => >.location,
228 }
229 }
230}
231
232#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
233#[serde(rename_all = "snake_case")]
234pub enum ConditionalKind {
235 Ifdef,
236 Ifndef,
237 If,
238 Elif,
239 Else,
240}
241
242#[derive(Debug, Clone, Serialize)]
245pub struct StructField {
246 pub field_type: TypeInfo,
247 #[serde(skip_serializing_if = "Option::is_none")]
249 pub field_name: Option<String>,
250 pub location: SourceLocation,
251 #[serde(default, skip_serializing_if = "Option::is_none")]
253 pub bit_width: Option<u32>,
254 #[serde(default, skip_serializing_if = "Vec::is_empty")]
258 pub inner_fields: Vec<Self>,
259}
260
261impl StructField {
262 pub fn is_reserved(&self) -> bool {
265 self.field_name.as_deref().is_some_and(|n| {
266 n.starts_with("rfu")
267 || n.starts_with("reserved")
268 || n.starts_with("padding")
269 || n.starts_with("_padding")
270 })
271 }
272
273 pub fn walk<F>(&self, f: &mut F)
276 where
277 F: FnMut(&Self),
278 {
279 f(self);
280 for inner in &self.inner_fields {
281 inner.walk(f);
282 }
283 }
284}
285
286#[derive(Debug, Clone, Serialize)]
288#[serde(rename_all = "snake_case")]
289pub enum TypedefTarget {
290 Type(TypeInfo),
292 Callback {
295 return_type: TypeInfo,
296 parameters: Vec<Parameter>,
297 },
298}
299
300impl TypedefTarget {
301 pub fn as_type(&self) -> Option<&TypeInfo> {
303 match self {
304 Self::Type(t) => Some(t),
305 Self::Callback { .. } => None,
306 }
307 }
308}
309
310#[derive(Debug, Clone, Serialize)]
311#[serde(rename_all = "snake_case")]
312pub enum TypeDefItem {
313 Typedef {
314 name: String,
315 target: TypedefTarget,
316 #[serde(default, skip_serializing_if = "Vec::is_empty")]
319 struct_fields: Vec<StructField>,
320 location: SourceLocation,
321 #[serde(skip_serializing_if = "Option::is_none")]
322 doc: Option<TypeDoc>,
323 },
324 Struct {
325 name: String,
326 #[serde(default, skip_serializing_if = "Vec::is_empty")]
327 fields: Vec<StructField>,
328 #[serde(default, skip_serializing_if = "Vec::is_empty")]
331 vfuncs: Vec<VirtualFunction>,
332 location: SourceLocation,
333 #[serde(skip_serializing_if = "Option::is_none")]
334 doc: Option<TypeDoc>,
335 },
336 Enum(Box<EnumInfo>),
337}
338
339impl TypeDefItem {
340 pub fn is_vtable_struct(&self) -> bool {
344 match self {
345 Self::Struct { name, vfuncs, .. } => {
346 let bare = name.trim_start_matches('_');
347 bare.ends_with("Class") || bare.ends_with("Interface") || !vfuncs.is_empty()
348 }
349 Self::Typedef { name, .. } => name.ends_with("Class") || name.ends_with("Interface"),
350 Self::Enum { .. } => false,
351 }
352 }
353}
354
355#[derive(Debug, Clone, Serialize)]
356pub struct FunctionDeclItem {
357 pub name: String,
358 pub return_type: TypeInfo,
359 #[serde(skip_serializing_if = "std::ops::Not::not")]
360 pub is_static: bool,
361 #[serde(skip_serializing_if = "std::ops::Not::not")]
362 pub is_inline: bool,
363 #[serde(skip_serializing_if = "Vec::is_empty")]
364 pub parameters: Vec<Parameter>,
365 #[serde(skip_serializing_if = "Vec::is_empty")]
366 pub export_macros: Vec<String>,
367 pub location: SourceLocation,
368 #[serde(skip_serializing_if = "Option::is_none")]
369 pub doc: Option<FunctionDoc>,
370}
371
372#[derive(Debug, Clone, Serialize)]
373pub struct FunctionDefItem {
374 pub name: String,
375 pub return_type: TypeInfo,
376 #[serde(skip_serializing_if = "std::ops::Not::not")]
377 pub is_static: bool,
378 #[serde(skip_serializing_if = "std::ops::Not::not")]
379 pub is_inline: bool,
380 #[serde(skip_serializing_if = "Vec::is_empty")]
381 pub parameters: Vec<Parameter>,
382 #[serde(skip_serializing_if = "Vec::is_empty")]
383 pub body_statements: Vec<Statement>,
384 pub location: SourceLocation,
385 #[serde(skip)]
386 pub body_location: Option<SourceLocation>,
387 #[serde(skip_serializing_if = "Option::is_none")]
388 pub doc: Option<FunctionDoc>,
389}
390
391impl FunctionDefItem {
392 pub fn find_calls<'a>(&'a self, function_names: &[&str]) -> Vec<&'a CallExpression> {
396 self.find_calls_matching(|name| function_names.contains(&name))
397 }
398
399 pub fn find_calls_matching<F>(&self, predicate: F) -> Vec<&CallExpression>
401 where
402 F: Fn(&str) -> bool,
403 {
404 let mut exprs: Vec<&Expression> = Vec::new();
405 for stmt in &self.body_statements {
406 stmt.walk_expressions(&mut |expr| exprs.push(expr));
407 }
408
409 let mut results = Vec::new();
410 for expr in exprs {
411 expr.walk(&mut |e| {
412 if let Expression::Call(call) = e
413 && call.function_name_str().is_some_and(&predicate)
414 {
415 results.push(call);
416 }
417 });
418 }
419 results
420 }
421
422 pub fn find_signal_registrations(&self, source: &[u8]) -> Vec<Signal> {
426 let mut signals = Vec::new();
427 let mut seen_names = std::collections::HashSet::new();
428
429 for (i, stmt) in self.body_statements.iter().enumerate() {
431 for assignment in stmt.iter_assignments() {
432 let Expression::Call(call) = &*assignment.rhs else {
433 continue;
434 };
435 if !call.function_contains("g_signal_new", source) {
436 continue;
437 }
438 let Some(mut signal) = Signal::from_g_signal_new_call(call, source) else {
439 continue;
440 };
441 if let Expression::Subscript(sub) = &*assignment.lhs
442 && let Expression::Identifier(id) = &*sub.index
443 {
444 signal.enum_value = Some(id.name.clone());
445 }
446 if i > 0
447 && let Statement::Comment(c) = &self.body_statements[i - 1]
448 {
449 signal.doc = SignalDoc::from_comment(c);
450 }
451 seen_names.insert(signal.name.clone());
452 signals.push(signal);
453 }
454 }
455
456 for (i, stmt) in self.body_statements.iter().enumerate() {
458 for call in stmt.iter_calls() {
459 if !call.function_name(source).starts_with("g_signal_new") {
460 continue;
461 }
462 let Some(name) = call.extract_string_from_arg(0) else {
463 continue;
464 };
465 if seen_names.contains(&name) {
466 continue;
467 }
468 if let Some(mut signal) = Signal::from_g_signal_new_call(call, source) {
469 if i > 0
470 && let Statement::Comment(c) = &self.body_statements[i - 1]
471 {
472 signal.doc = SignalDoc::from_comment(c);
473 }
474 signals.push(signal);
475 }
476 }
477 }
478
479 signals
480 }
481
482 pub fn iter_local_declarations(&self) -> impl Iterator<Item = &VariableDecl> {
484 self.body_statements
485 .iter()
486 .flat_map(Statement::iter_declarations)
487 }
488
489 pub fn collect_return_values(&self) -> Vec<&Expression> {
491 self.body_statements
492 .iter()
493 .flat_map(Statement::iter_returns)
494 .filter_map(|r| r.value.as_ref())
495 .collect()
496 }
497
498 pub fn is_var_returned(&self, type_info: &TypeInfo) -> bool {
501 for stmt in &self.body_statements {
502 for ret in stmt.iter_returns() {
503 if let Some(Expression::Identifier(id)) = &ret.value {
504 for body_stmt in &self.body_statements {
506 for decl in body_stmt.iter_declarations() {
507 if decl.name == id.name
508 && decl.type_info.base_type == type_info.base_type
509 && decl.type_info.is_pointer() == type_info.is_pointer()
510 {
511 return true;
512 }
513 }
514 }
515 }
516 }
517 }
518 false
519 }
520
521 pub fn is_var_passed_to_cleanup(&self, type_info: &TypeInfo) -> bool {
524 for stmt in &self.body_statements {
525 for call in stmt.iter_calls() {
526 if call.is_cleanup_call()
527 && let Some(arg) = call.get_arg(0)
528 && let Expression::Identifier(id) = arg
529 {
530 for body_stmt in &self.body_statements {
532 for decl in body_stmt.iter_declarations() {
533 if decl.name == id.name
534 && decl.type_info.base_type == type_info.base_type
535 && decl.type_info.is_pointer() == type_info.is_pointer()
536 {
537 return true;
538 }
539 }
540 }
541 }
542 }
543 }
544 false
545 }
546
547 pub fn is_var_passed_to_function(
550 &self,
551 var_name: &str,
552 func_name: &str,
553 arg_index: usize,
554 ) -> bool {
555 self.body_statements.iter().any(|stmt| {
556 stmt.iter_calls().any(|call| {
557 call.is_function(func_name)
558 && call.get_arg(arg_index).is_some_and(
559 |arg| matches!(arg, Expression::Identifier(id) if id.name == var_name),
560 )
561 })
562 })
563 }
564
565 pub fn is_var_allocated(&self, type_info: &TypeInfo) -> bool {
569 self.is_var_allocated_with(type_info, CallExpression::is_allocation_call)
570 }
571
572 pub fn is_var_allocated_with(
575 &self,
576 type_info: &TypeInfo,
577 is_allocation: impl Fn(&CallExpression) -> bool,
578 ) -> bool {
579 for stmt in &self.body_statements {
580 let mut found = false;
581 stmt.walk(&mut |s| {
582 match s {
583 Statement::Declaration(decl) => {
585 if decl.type_info.base_type == type_info.base_type
586 && decl.type_info.is_pointer() == type_info.is_pointer()
587 && let Some(Expression::Call(call)) = &decl.initializer
588 && is_allocation(call)
589 {
590 found = true;
591 }
592 }
593 Statement::Expression(expr_stmt) => {
595 if let Expression::Assignment(assign) = expr_stmt.as_ref()
596 && let Expression::Identifier(id) = &*assign.lhs
597 && let Expression::Call(call) = &*assign.rhs
598 && is_allocation(call)
599 {
600 for body_stmt in &self.body_statements {
602 for decl in body_stmt.iter_declarations() {
603 if decl.name == id.name
604 && decl.type_info.base_type == type_info.base_type
605 && decl.type_info.is_pointer() == type_info.is_pointer()
606 {
607 found = true;
608 }
609 }
610 }
611 }
612 }
613 _ => {}
614 }
615 });
616 if found {
617 return true;
618 }
619 }
620 false
621 }
622
623 pub fn find_install_properties_calls(&self) -> Vec<&CallExpression> {
625 self.find_calls(&["g_object_class_install_properties"])
626 }
627
628 pub fn local_var_types(&self) -> std::collections::HashMap<&str, &TypeInfo> {
632 let mut map = std::collections::HashMap::new();
633 for param in &self.parameters {
634 if let Parameter::Regular {
635 name: Some(name),
636 type_info,
637 ..
638 } = param
639 {
640 map.insert(name.as_str(), type_info);
641 }
642 }
643 for stmt in &self.body_statements {
644 stmt.walk(&mut |s| {
645 if let Statement::Declaration(decl) = s {
646 map.insert(decl.name.as_str(), &decl.type_info);
647 }
648 });
649 }
650 map
651 }
652
653 pub fn get_param_by_name(&self, name: &str) -> Option<&Parameter> {
655 self.parameters
656 .iter()
657 .find(|p| matches!(p, Parameter::Regular { name: Some(n), .. } if n == name))
658 }
659
660 pub(crate) fn find_param_spec_assignments(&self, source: &[u8]) -> Vec<ParamSpecAssignment> {
665 let mut assignments = Vec::new();
666 let mut array_assignments: HashMap<&str, Vec<usize>> = HashMap::new();
667 let mut variable_assignments: HashMap<&str, Vec<usize>> = HashMap::new();
668
669 for (i, stmt) in self.body_statements.iter().enumerate() {
671 stmt.walk(&mut |s| {
672 if let Statement::Expression(expr_stmt) = s {
673 match expr_stmt.as_ref() {
674 Expression::Assignment(assignment) => {
676 if let Expression::Call(param_call) = &*assignment.rhs {
677 let func_name = param_call.function_name(source);
678 if !func_name.contains("_param_spec_") {
679 return;
680 }
681
682 let Some(mut property) =
684 Property::from_param_spec_call(param_call, source)
685 else {
686 return;
687 };
688 if i > 0
689 && let Statement::Comment(c) = &self.body_statements[i - 1]
690 {
691 property.doc = PropertyDoc::from_comment(c);
692 }
693
694 if let Expression::Subscript(subscript) = &*assignment.lhs {
696 if let Some(array_name) =
698 subscript.array.to_source_string(source)
699 && let Some(enum_value) =
700 subscript.index.to_source_string(source)
701 {
702 let idx = assignments.len();
703 array_assignments.entry(array_name).or_default().push(idx);
704 assignments.push(ParamSpecAssignment::ArraySubscript {
705 array_name: array_name.to_owned(),
706 enum_value: enum_value.to_owned(),
707 statement_location: *s.location(),
708 call: param_call.clone(),
709 property,
710 install_call: None,
711 });
712 }
713 } else if let Some(var_name) =
714 assignment.lhs.to_source_string(source)
715 {
716 let idx = assignments.len();
718 variable_assignments.entry(var_name).or_default().push(idx);
719 assignments.push(ParamSpecAssignment::Variable {
720 variable_name: var_name.to_owned(),
721 statement_location: *s.location(),
722 call: param_call.clone(),
723 property,
724 install_call: None,
725 });
726 }
727 }
728 }
729 Expression::Call(call) => {
731 if call.function_contains("override_property", source)
732 && let Some(mut property) =
733 Property::from_override_property_call(call)
734 && let Some(enum_arg) = call.get_arg(1)
735 && let Some(enum_value) = enum_arg.to_source_string(source)
736 {
737 if i > 0
738 && let Statement::Comment(c) = &self.body_statements[i - 1]
739 {
740 property.doc = PropertyDoc::from_comment(c);
741 }
742 assignments.push(ParamSpecAssignment::OverrideProperty {
743 enum_value: enum_value.to_owned(),
744 statement_location: *s.location(),
745 call: call.clone(),
746 property,
747 });
748 }
749 }
750 _ => {}
751 }
752 }
753 });
754 }
755
756 for (i, stmt) in self.body_statements.iter().enumerate() {
758 stmt.walk(&mut |s| {
759 if let Statement::Expression(expr_stmt) = s
760 && let Expression::Call(call) = expr_stmt.as_ref()
761 {
762 if call.function_contains("install_properties", source) {
764 if let Some(array_arg) = call.get_arg(2)
765 && let Some(array_name) = array_arg.to_source_string(source)
766 && let Some(indices) = array_assignments.get(&array_name)
767 {
768 for &idx in indices {
769 if let ParamSpecAssignment::ArraySubscript {
770 install_call, ..
771 } = &mut assignments[idx]
772 {
773 *install_call = Some(call.clone());
774 }
775 }
776 }
777 }
778 else if call.function_contains("install_property", source)
780 && let Some(spec_expr) = call.get_arg(2)
781 {
782 if let Expression::Call(spec_call) = spec_expr
783 && spec_call.function_contains("_param_spec_", source)
784 && let Some(enum_arg) = call.get_arg(1)
785 && let Some(enum_value) = enum_arg.to_source_string(source)
786 && let Some(mut property) =
787 Property::from_param_spec_call(spec_call, source)
788 {
789 if i > 0
790 && let Statement::Comment(c) = &self.body_statements[i - 1]
791 {
792 property.doc = PropertyDoc::from_comment(c);
793 }
794 assignments.push(ParamSpecAssignment::DirectInstall {
795 enum_value: enum_value.to_owned(),
796 statement_location: *s.location(),
797 call: spec_call.clone(),
798 property,
799 install_call: call.clone(),
800 });
801 } else if let Some(var_name) = spec_expr.to_source_string(source)
802 && let Some(indices) = variable_assignments.get(var_name)
803 {
804 let indices = indices.clone();
805 for idx in indices {
806 if let ParamSpecAssignment::Variable { install_call, .. } =
807 &mut assignments[idx]
808 {
809 *install_call = Some(call.clone());
810 }
811 }
812 }
813 }
814 }
815 });
816 }
817
818 assignments
819 }
820}