1use std::{
7 borrow::Borrow,
8 collections::{HashMap, HashSet},
9};
10
11use log::warn;
12
13use super::{namespaces::NsId, special_functions};
14use crate::{
15 analysis::{
16 self,
17 bounds::{Bounds, CallbackInfo},
18 function_parameters::{self, CParameter, Parameters, Transformation, TransformationType},
19 imports::Imports,
20 is_gpointer,
21 out_parameters::{self, use_function_return_for_result},
22 ref_mode::RefMode,
23 return_value,
24 rust_type::*,
25 safety_assertion_mode::SafetyAssertionMode,
26 signatures::{Signature, Signatures},
27 trampolines::Trampoline,
28 },
29 codegen::Visibility,
30 config::{self, gobjects::GStatus},
31 env::Env,
32 library::{self, Function, FunctionKind, MAIN_NAMESPACE, ParameterDirection, Type},
33 nameutil,
34 traits::*,
35 version::Version,
36};
37
38#[derive(Clone, Debug)]
39pub struct AsyncTrampoline {
40 pub is_method: bool,
41 pub has_error_parameter: bool,
42 pub name: String,
43 pub finish_func_name: String,
44 pub callback_type: String,
45 pub bound_name: char,
46 pub output_params: Vec<analysis::Parameter>,
47 pub ffi_ret: Option<analysis::Parameter>,
48}
49
50#[derive(Clone, Debug)]
51pub struct AsyncFuture {
52 pub is_method: bool,
53 pub name: String,
54 pub success_parameters: String,
55 pub error_parameters: Option<String>,
56 pub assertion: SafetyAssertionMode,
57}
58
59#[derive(Debug)]
60pub struct Info {
61 pub name: String,
62 pub func_name: String,
63 pub new_name: Option<String>,
64 pub glib_name: String,
65 pub status: GStatus,
66 pub kind: library::FunctionKind,
67 pub visibility: Visibility,
68 pub type_name: Result,
69 pub parameters: Parameters,
70 pub ret: return_value::Info,
71 pub bounds: Bounds,
72 pub outs: out_parameters::Info,
73 pub version: Option<Version>,
74 pub deprecated_version: Option<Version>,
75 pub not_version: Option<Version>,
76 pub cfg_condition: Option<String>,
77 pub assertion: SafetyAssertionMode,
78 pub doc_hidden: bool,
79 pub doc_trait_name: Option<String>,
80 pub doc_struct_name: Option<String>,
81 pub doc_ignore_parameters: HashSet<String>,
82 pub r#async: bool,
83 pub unsafe_: bool,
84 pub trampoline: Option<AsyncTrampoline>,
85 pub callbacks: Vec<Trampoline>,
86 pub destroys: Vec<Trampoline>,
87 pub remove_params: Vec<usize>,
88 pub async_future: Option<AsyncFuture>,
89 pub hidden: bool,
92 pub commented: bool,
94 pub ns_id: NsId,
97 pub generate_doc: bool,
98 pub get_property: Option<String>,
99 pub set_property: Option<String>,
100}
101
102impl Info {
103 pub fn codegen_name(&self) -> &str {
104 self.new_name.as_ref().unwrap_or(&self.name)
105 }
106
107 pub fn is_special(&self) -> bool {
108 self.codegen_name()
109 .trim_end_matches('_')
110 .rsplit('_')
111 .next()
112 .is_some_and(|i| i.parse::<special_functions::Type>().is_ok())
113 }
114
115 pub fn should_be_doc_linked(&self, env: &Env) -> bool {
117 self.should_docs_be_generated(env)
118 && (self.status.manual() || (!self.commented && !self.hidden))
119 }
120
121 pub fn should_docs_be_generated(&self, env: &Env) -> bool {
122 !self.status.ignored() && !self.is_special() && !self.is_async_finish(env)
123 }
124
125 pub fn doc_link(
126 &self,
127 parent: Option<&str>,
128 visible_parent: Option<&str>,
129 is_self: bool,
130 ) -> String {
131 if let Some(p) = parent {
132 if is_self {
133 format!("[`{f}()`][Self::{f}()]", f = self.codegen_name())
134 } else {
135 format!(
136 "[`{visible_parent}::{f}()`][crate::{p}::{f}()]",
137 visible_parent = visible_parent.unwrap_or(p),
138 p = p,
139 f = self.codegen_name()
140 )
141 }
142 } else {
143 format!(
144 "[`{fn_name}()`][crate::{fn_name}()]",
145 fn_name = self.codegen_name()
146 )
147 }
148 }
149
150 pub fn is_async_finish(&self, env: &Env) -> bool {
151 let has_async_result = self
152 .parameters
153 .rust_parameters
154 .iter()
155 .any(|param| param.typ.full_name(&env.library) == "Gio.AsyncResult");
156 self.name.ends_with("_finish") && has_async_result
157 }
158}
159
160pub fn analyze<F: Borrow<library::Function>>(
161 env: &Env,
162 functions: &[F],
163 type_tid: Option<library::TypeId>,
164 in_trait: bool,
165 is_boxed: bool,
166 obj: &config::gobjects::GObject,
167 imports: &mut Imports,
168 mut signatures: Option<&mut Signatures>,
169 deps: Option<&[library::TypeId]>,
170) -> Vec<Info> {
171 let mut funcs = Vec::new();
172
173 'func: for func in functions {
174 let func = func.borrow();
175 let configured_functions = obj.functions.matched(&func.name);
176 let mut status = obj.status;
177 for f in &configured_functions {
178 match f.status {
179 GStatus::Ignore => continue 'func,
180 GStatus::Manual => {
181 status = GStatus::Manual;
182 break;
183 }
184 GStatus::Generate => (),
185 }
186 }
187
188 if env.is_totally_deprecated(
189 Some(type_tid.unwrap_or_default().ns_id),
190 func.deprecated_version,
191 ) {
192 continue;
193 }
194 let name = nameutil::mangle_keywords(&*func.name).into_owned();
195 let signature_params = Signature::new(func);
196 let mut not_version = None;
197 if func.kind == library::FunctionKind::Method
198 && let Some(deps) = deps
199 {
200 let (has, version) = signature_params.has_in_deps(env, &name, deps);
201 if has
202 && let Some(v) = version
203 && v > env.config.min_cfg_version
204 {
205 not_version = version;
206 }
207 }
208 if let Some(signatures) = signatures.as_mut() {
209 signatures.insert(name.clone(), signature_params);
210 }
211
212 let mut info = analyze_function(
213 env,
214 obj,
215 &func.name,
216 name,
217 status,
218 func,
219 type_tid,
220 in_trait,
221 is_boxed,
222 &configured_functions,
223 imports,
224 );
225 info.not_version = not_version;
226 funcs.push(info);
227 }
228
229 funcs
230}
231
232fn fixup_gpointer_parameter(
233 env: &Env,
234 type_tid: library::TypeId,
235 is_boxed: bool,
236 in_trait: bool,
237 parameters: &mut Parameters,
238 idx: usize,
239) {
240 use crate::analysis::ffi_type;
241
242 let is_instance_parameter = idx == 0;
243
244 let glib_name = env.library.type_(type_tid).get_glib_name().unwrap();
245 let ffi_name = ffi_type::ffi_type(env, type_tid, glib_name).unwrap();
246 let pointer_type = if is_boxed { "*const" } else { "*mut" };
247 parameters.rust_parameters[idx].typ = type_tid;
248 parameters.c_parameters[idx].typ = type_tid;
249 parameters.c_parameters[idx].is_instance_parameter = is_instance_parameter;
250 parameters.c_parameters[idx].ref_mode = RefMode::ByRef;
251 parameters.c_parameters[idx].transfer = gir_parser::TransferOwnership::None;
252 parameters.transformations[idx] = Transformation {
253 ind_c: idx,
254 ind_rust: Some(idx),
255 transformation_type: TransformationType::ToGlibPointer {
256 name: parameters.rust_parameters[idx].name.clone(),
257 is_instance_parameter,
258 transfer: gir_parser::TransferOwnership::None,
259 ref_mode: RefMode::ByRef,
260 to_glib_extra: Default::default(),
261 explicit_target_type: format!("{} {}", pointer_type, ffi_name.as_str()),
262 pointer_cast: format!(
263 " as {}",
264 nameutil::use_glib_if_needed(env, "ffi::gconstpointer")
265 ),
266 in_trait,
267 nullable: false,
268 move_: false,
269 },
270 };
271}
272
273fn fixup_special_functions(
274 env: &Env,
275 name: &str,
276 type_tid: library::TypeId,
277 is_boxed: bool,
278 in_trait: bool,
279 parameters: &mut Parameters,
280) {
281 if name == "hash"
284 && parameters.c_parameters.len() == 1
285 && parameters.c_parameters[0].c_type == "gconstpointer"
286 {
287 fixup_gpointer_parameter(env, type_tid, is_boxed, in_trait, parameters, 0);
288 }
289
290 if (name == "compare" || name == "equal" || name == "is_equal")
291 && parameters.c_parameters.len() == 2
292 && parameters.c_parameters[0].c_type == "gconstpointer"
293 && parameters.c_parameters[1].c_type == "gconstpointer"
294 {
295 fixup_gpointer_parameter(env, type_tid, is_boxed, in_trait, parameters, 0);
296 fixup_gpointer_parameter(env, type_tid, is_boxed, in_trait, parameters, 1);
297 }
298}
299
300fn find_callback_bound_to_destructor(
301 callbacks: &[Trampoline],
302 destroy: &mut Trampoline,
303 destroy_index: usize,
304) -> bool {
305 for call in callbacks {
306 if call.destroy_index == destroy_index {
307 destroy.nullable = call.nullable;
308 destroy.bound_name = call.bound_name.clone();
309 return true;
310 }
311 }
312 false
313}
314
315fn analyze_callbacks(
316 env: &Env,
317 func: &library::Function,
318 cross_user_data_check: &mut HashMap<usize, usize>,
319 user_data_indexes: &mut HashSet<usize>,
320 parameters: &mut Parameters,
321 used_types: &mut Vec<String>,
322 bounds: &mut Bounds,
323 to_glib_extras: &mut HashMap<usize, String>,
324 imports: &mut Imports,
325 destroys: &mut Vec<Trampoline>,
326 callbacks: &mut Vec<Trampoline>,
327 params: &mut Vec<library::Parameter>,
328 configured_functions: &[&config::functions::Function],
329 disable_length_detect: bool,
330 in_trait: bool,
331 commented: &mut bool,
332 concurrency: library::Concurrency,
333 type_tid: library::TypeId,
334) {
335 let mut to_replace = Vec::new();
336 let mut to_remove = Vec::new();
337
338 {
339 let mut c_parameters = Vec::new();
342 for (pos, par) in parameters.c_parameters.iter().enumerate() {
343 if par.is_instance_parameter {
344 continue;
345 }
346 c_parameters.push((par, pos));
347 }
348
349 let func_name = &func.c_identifier;
350 let mut destructors_to_update = Vec::new();
351 for pos in 0..parameters.c_parameters.len() {
352 if cross_user_data_check.values().any(|p| *p == pos) || user_data_indexes.contains(&pos)
354 {
355 continue;
356 }
357 let par = ¶meters.c_parameters[pos];
358 assert!(
359 !par.is_instance_parameter || pos == 0,
360 "Wrong instance parameter in {}",
361 func.c_identifier
362 );
363 if let Ok(rust_type) = RustType::builder(env, par.typ)
364 .direction(par.direction)
365 .try_from_glib(&par.try_from_glib)
366 .try_build()
367 {
368 used_types.extend(rust_type.into_used_types());
369 }
370 let rust_type = env.library.type_(par.typ);
371 let callback_info = if !par.nullable || !rust_type.is_function() {
372 let (to_glib_extra, callback_info) = bounds.add_for_parameter(
373 env,
374 func,
375 par,
376 false,
377 concurrency,
378 configured_functions,
379 );
380 if let Some(to_glib_extra) = to_glib_extra {
381 let pos_adjusted_for_removed_params =
382 pos - to_remove.len() - cross_user_data_check.len();
383 if par.c_type != "GDestroyNotify" {
384 to_glib_extras.insert(pos_adjusted_for_removed_params, to_glib_extra);
385 }
386 }
387 callback_info
388 } else {
389 None
390 };
391
392 if rust_type.is_function() {
393 if par.c_type != "GDestroyNotify" {
394 let callback_parameters_config = configured_functions.iter().find_map(|f| {
395 f.parameters
396 .iter()
397 .find(|p| p.ident.is_match(&par.name))
398 .map(|p| &p.callback_parameters)
399 });
400 if let Some((mut callback, destroy_index)) = analyze_callback(
401 func_name,
402 type_tid,
403 env,
404 par,
405 &callback_info,
406 commented,
407 imports,
408 &c_parameters,
409 rust_type,
410 callback_parameters_config,
411 ) {
412 if let Some(destroy_index) = destroy_index {
413 let user_data = cross_user_data_check
414 .entry(destroy_index)
415 .or_insert_with(|| callback.user_data_index);
416 if *user_data != callback.user_data_index {
417 warn_main!(
418 type_tid,
419 "`{}`: Different destructors cannot share the same user data",
420 func_name
421 );
422 *commented = true;
423 }
424 callback.destroy_index = destroy_index;
425 } else {
426 user_data_indexes.insert(callback.user_data_index);
427 to_remove.push(callback.user_data_index);
428 }
429 callbacks.push(callback);
430 to_replace.push((pos, par.typ));
431 continue;
432 }
433 } else if let Some((mut callback, _)) = analyze_callback(
434 func_name,
435 type_tid,
436 env,
437 par,
438 &callback_info,
439 commented,
440 imports,
441 &c_parameters,
442 rust_type,
443 None,
444 ) {
445 if let Some(user_data_index) = cross_user_data_check.get(&pos) {
448 callback.user_data_index = *user_data_index;
449 callback.destroy_index = pos;
450 } else {
451 warn_main!(
452 type_tid,
453 "`{}`: no user data point to the destroy callback",
454 func_name,
455 );
456 *commented = true;
457 }
458 if !find_callback_bound_to_destructor(callbacks, &mut callback, pos) {
461 destructors_to_update.push((pos, destroys.len()));
463 }
464 destroys.push(callback);
465 to_remove.push(pos);
466 continue;
467 }
468 }
469 if !*commented {
470 *commented |= RustType::builder(env, par.typ)
471 .direction(par.direction)
472 .scope(par.scope)
473 .try_from_glib(&par.try_from_glib)
474 .try_build_param()
475 .is_err();
476 }
477 }
478 for (destroy_index, pos_in_destroys) in destructors_to_update {
479 if !find_callback_bound_to_destructor(
480 callbacks,
481 &mut destroys[pos_in_destroys],
482 destroy_index,
483 ) {
484 warn_main!(
485 type_tid,
486 "`{}`: destructor without linked callback",
487 func_name
488 );
489 }
490 }
491 }
492
493 if cross_user_data_check
495 .values()
496 .collect::<Vec<_>>()
497 .windows(2)
498 .any(|a| a[0] == a[1])
499 {
500 *commented = true;
501 warn_main!(
502 type_tid,
503 "`{}`: Different user data share the same destructors",
504 func.name
505 );
506 }
507
508 if !destroys.is_empty() || !callbacks.is_empty() {
509 for (pos, typ) in to_replace {
510 let ty = env.library.type_(typ);
511 params[pos].set_tid(typ);
512 params[pos].set_c_type(ty.get_glib_name().unwrap());
513 }
514 let mut s = to_remove
515 .iter()
516 .chain(cross_user_data_check.values())
517 .collect::<HashSet<_>>() .into_iter()
519 .collect::<Vec<_>>();
520 s.sort(); for pos in s.iter().rev() {
523 params.remove(**pos);
524 }
525 *parameters = function_parameters::analyze(
526 env,
527 params,
528 configured_functions,
529 disable_length_detect,
530 false,
531 in_trait,
532 );
533 } else {
534 warn_main!(
535 type_tid,
536 "`{}`: this is supposed to be a callback function but no callback was found...",
537 func.name
538 );
539 *commented = true;
540 }
541}
542
543fn analyze_function(
544 env: &Env,
545 obj: &config::gobjects::GObject,
546 func_name: &str,
547 name: String,
548 status: GStatus,
549 func: &library::Function,
550 type_tid: Option<library::TypeId>,
551 in_trait: bool,
552 is_boxed: bool,
553 configured_functions: &[&config::functions::Function],
554 imports: &mut Imports,
555) -> Info {
556 let ns_id = type_tid.map_or(MAIN_NAMESPACE, |t| t.ns_id);
557 let type_tid = type_tid.unwrap_or_default();
558 let r#async = func.finish_func.is_some()
559 || func.parameters.iter().any(|parameter| {
560 parameter
561 .scope()
562 .is_some_and(|s| s == gir_parser::FunctionScope::Async)
563 && parameter.c_type() == "GAsyncReadyCallback"
564 });
565 let has_callback_parameter = !r#async
566 && func
567 .parameters
568 .iter()
569 .any(|par| env.library.type_(par.typ()).is_function());
570 let concurrency = match env.library.type_(type_tid) {
571 library::Type::Class(_) | library::Type::Interface(_) | library::Type::Record(_) => {
572 obj.concurrency
573 }
574 _ => library::Concurrency::SendSync,
575 };
576
577 let mut commented = false;
578 let mut bounds: Bounds = Default::default();
579 let mut to_glib_extras = HashMap::<usize, String>::new();
580 let mut used_types: Vec<String> = Vec::with_capacity(4);
581 let mut trampoline = None;
582 let mut callbacks = Vec::new();
583 let mut destroys = Vec::new();
584 let mut async_future = None;
585
586 if status.need_generate()
587 && !r#async
588 && !has_callback_parameter
589 && func
590 .parameters
591 .iter()
592 .any(|par| par.c_type() == "GDestroyNotify")
593 {
594 warn_main!(
598 type_tid,
599 "Function \"{}\" with destroy callback without callbacks",
600 func.name
601 );
602 commented = true;
603 }
604
605 let mut new_name = configured_functions.iter().find_map(|f| f.rename.clone());
606 let is_constructor = configured_functions.iter().find_map(|f| f.is_constructor);
607
608 let bypass_auto_rename = configured_functions.iter().any(|f| f.bypass_auto_rename);
609 let is_constructor = is_constructor.unwrap_or(false);
610 if !bypass_auto_rename && new_name.is_none() {
611 if func.kind == library::FunctionKind::Constructor || is_constructor {
612 if func.kind == library::FunctionKind::Constructor && is_constructor {
613 warn_main!(
614 type_tid,
615 "`{}`: config forces 'constructor' on an already gir-annotated 'constructor'",
616 func_name
617 );
618 }
619
620 if name.starts_with("new_from")
621 || name.starts_with("new_with")
622 || name.starts_with("new_for")
623 {
624 new_name = Some(name[4..].to_string());
625 }
626 } else {
627 let nb_in_params = func
628 .parameters
629 .iter()
630 .filter(|param| param.direction().is_in())
631 .fold(0, |acc, _| acc + 1);
632 let is_bool_getter = (func.parameters.len() == nb_in_params)
633 && (func.ret.typ() == library::TypeId::tid_bool()
634 || func.ret.typ() == library::TypeId::tid_c_bool());
635 new_name = getter_rules::try_rename_would_be_getter(&name, is_bool_getter)
636 .ok()
637 .map(getter_rules::NewName::unwrap);
638 }
639 }
640
641 let version = configured_functions
642 .iter()
643 .filter_map(|f| f.version)
644 .min()
645 .or(func.version);
646
647 let version = env.config.filter_version(version);
648 let deprecated_version = func.deprecated_version;
649 let visibility = configured_functions
650 .iter()
651 .find_map(|f| f.visibility)
652 .unwrap_or_default();
653 let cfg_condition = configured_functions
654 .iter()
655 .find_map(|f| f.cfg_condition.clone());
656 let doc_hidden = configured_functions.iter().any(|f| f.doc_hidden);
657 let doc_trait_name = configured_functions
658 .iter()
659 .find_map(|f| f.doc_trait_name.clone());
660 let doc_struct_name = configured_functions
661 .iter()
662 .find_map(|f| f.doc_struct_name.clone());
663 let doc_ignore_parameters = configured_functions
664 .iter()
665 .find(|f| !f.doc_ignore_parameters.is_empty())
666 .map(|f| f.doc_ignore_parameters.clone())
667 .unwrap_or_default();
668 let disable_length_detect = configured_functions.iter().any(|f| f.disable_length_detect);
669 let no_future = configured_functions.iter().any(|f| f.no_future);
670 let unsafe_ = configured_functions.iter().any(|f| f.unsafe_);
671 let assertion = configured_functions.iter().find_map(|f| f.assertion);
672
673 let imports = &mut imports.with_defaults(version, &cfg_condition);
674
675 let ret = return_value::analyze(
676 env,
677 obj,
678 func,
679 type_tid,
680 configured_functions,
681 &mut used_types,
682 imports,
683 );
684 commented |= ret.commented;
685
686 let mut params = func.parameters.clone();
687 let mut parameters = function_parameters::analyze(
688 env,
689 ¶ms,
690 configured_functions,
691 disable_length_detect,
692 r#async,
693 in_trait,
694 );
695 parameters.analyze_return(env, &ret.parameter);
696
697 if status.need_generate()
698 && let Some(ref f) = ret.parameter
699 && let Type::Function(_) = env.library.type_(f.lib_par.typ())
700 && env.config.work_mode.is_normal()
701 {
702 warn!("Function \"{}\" returns callback", func.name);
703 commented = true;
704 }
705
706 fixup_special_functions(
707 env,
708 name.as_str(),
709 type_tid,
710 is_boxed,
711 in_trait,
712 &mut parameters,
713 );
714
715 let mut cross_user_data_check: HashMap<usize, usize> = HashMap::new();
718 let mut user_data_indexes: HashSet<usize> = HashSet::new();
719
720 if status.need_generate() {
721 if !has_callback_parameter {
722 let mut to_remove = Vec::new();
723 let mut correction_instance = 0;
724 for par in parameters.c_parameters.iter() {
725 if par.scope.is_none() {
726 continue;
727 }
728 if let Some(index) = par.user_data_index {
729 to_remove.push(index);
730 }
731 if let Some(index) = par.destroy_index {
732 to_remove.push(index);
733 }
734 }
735 for (pos, par) in parameters.c_parameters.iter().enumerate() {
736 if par.is_instance_parameter {
737 correction_instance = 1;
738 }
739
740 if r#async
741 && pos >= correction_instance
742 && to_remove.contains(&(pos - correction_instance))
743 {
744 continue;
745 }
746 assert!(
747 !par.is_instance_parameter || pos == 0,
748 "Wrong instance parameter in {}",
749 func.c_identifier
750 );
751 if let Ok(rust_type) = RustType::builder(env, par.typ)
752 .direction(par.direction)
753 .try_from_glib(&par.try_from_glib)
754 .try_build()
755 && (!rust_type.as_str().ends_with("GString") || par.c_type == "gchar***")
756 {
757 used_types.extend(rust_type.into_used_types());
758 }
759 let (to_glib_extra, callback_info) = bounds.add_for_parameter(
760 env,
761 func,
762 par,
763 r#async,
764 library::Concurrency::None,
765 configured_functions,
766 );
767 if let Some(to_glib_extra) = to_glib_extra {
768 to_glib_extras.insert(pos, to_glib_extra);
769 }
770
771 analyze_async(
772 env,
773 func,
774 type_tid,
775 new_name.as_ref().unwrap_or(&name),
776 callback_info,
777 &mut commented,
778 &mut trampoline,
779 no_future,
780 &mut async_future,
781 configured_functions,
782 ¶meters,
783 );
784 let type_error = !(r#async
785 && *env.library.type_(par.typ) == Type::Basic(library::Basic::Pointer))
786 && RustType::builder(env, par.typ)
787 .direction(par.direction)
788 .scope(par.scope)
789 .try_from_glib(&par.try_from_glib)
790 .try_build_param()
791 .is_err();
792 if type_error {
793 commented = true;
794 }
795 }
796 if r#async && trampoline.is_none() {
797 commented = true;
798 }
799 } else {
800 analyze_callbacks(
801 env,
802 func,
803 &mut cross_user_data_check,
804 &mut user_data_indexes,
805 &mut parameters,
806 &mut used_types,
807 &mut bounds,
808 &mut to_glib_extras,
809 imports,
810 &mut destroys,
811 &mut callbacks,
812 &mut params,
813 configured_functions,
814 disable_length_detect,
815 in_trait,
816 &mut commented,
817 concurrency,
818 type_tid,
819 );
820 }
821 }
822
823 for par in ¶meters.rust_parameters {
824 let is_len_for_par = |t: &Transformation| {
826 if let TransformationType::Length { ref array_name, .. } = t.transformation_type {
827 array_name == &par.name
828 } else {
829 false
830 }
831 };
832 if is_carray_with_direct_elements(env, par.typ)
833 && !parameters.transformations.iter().any(is_len_for_par)
834 {
835 commented = true;
836 }
837 }
838
839 let (outs, unsupported_outs) = out_parameters::analyze(
840 env,
841 func,
842 ¶meters.c_parameters,
843 &ret,
844 configured_functions,
845 );
846 if unsupported_outs && status.need_generate() {
847 warn_main!(
848 type_tid,
849 "Function {} has unsupported outs",
850 func.c_identifier
851 );
852 commented = true;
853 }
854
855 if r#async && status.need_generate() && !commented {
856 imports.add("std::boxed::Box as Box_");
857 imports.add("std::pin::Pin");
858
859 if let Some(ref trampoline) = trampoline {
860 for out in &trampoline.output_params {
861 if let Ok(rust_type) = RustType::builder(env, out.lib_par.typ())
862 .direction(ParameterDirection::Out)
863 .try_build()
864 {
865 used_types.extend(rust_type.into_used_types());
866 }
867 }
868 if let Some(ref out) = trampoline.ffi_ret
869 && let Ok(rust_type) = RustType::builder(env, out.lib_par.typ())
870 .direction(ParameterDirection::Return)
871 .try_build()
872 {
873 used_types.extend(rust_type.into_used_types());
874 }
875 }
876 }
877
878 if status.need_generate() && !commented {
879 if (!destroys.is_empty() || !callbacks.is_empty())
880 && callbacks
881 .iter()
882 .any(|c| !c.scope.is_some_and(|s| s.is_call()))
883 {
884 imports.add("std::boxed::Box as Box_");
885 }
886
887 for transformation in &mut parameters.transformations {
888 if let Some(to_glib_extra) = to_glib_extras.get(&transformation.ind_c) {
889 transformation
890 .transformation_type
891 .set_to_glib_extra(to_glib_extra);
892 }
893 }
894
895 imports.add("crate::ffi");
896
897 imports.add_used_types(&used_types);
898 if ret.base_tid.is_some() {
899 imports.add("glib::prelude::*");
900 }
901
902 if func.name.parse::<special_functions::Type>().is_err()
903 || parameters.c_parameters.iter().any(|p| p.move_)
904 {
905 imports.add("glib::translate::*");
906 }
907 bounds.update_imports(imports);
908 }
909
910 let is_method = func.kind == library::FunctionKind::Method;
911 let assertion =
912 assertion.unwrap_or_else(|| SafetyAssertionMode::of(env, is_method, ¶meters));
913
914 let generate_doc = configured_functions.iter().all(|f| f.generate_doc);
915
916 Info {
917 name,
918 func_name: func_name.to_string(),
919 new_name,
920 glib_name: func.c_identifier.clone(),
921 status,
922 kind: func.kind,
923 visibility,
924 type_name: RustType::try_new(env, type_tid),
925 parameters,
926 ret,
927 bounds,
928 outs,
929 version,
930 deprecated_version,
931 not_version: None,
932 cfg_condition,
933 assertion,
934 doc_hidden,
935 doc_trait_name,
936 doc_struct_name,
937 doc_ignore_parameters,
938 r#async,
939 unsafe_,
940 trampoline,
941 async_future,
942 callbacks,
943 destroys,
944 remove_params: cross_user_data_check.values().copied().collect::<Vec<_>>(),
945 commented,
946 hidden: false,
947 ns_id,
948 generate_doc,
949 get_property: func.get_property.clone(),
950 set_property: func.set_property.clone(),
951 }
952}
953
954pub fn is_carray_with_direct_elements(env: &Env, typ: library::TypeId) -> bool {
955 match *env.library.type_(typ) {
956 Type::CArray(inner_tid) => {
957 use super::conversion_type::ConversionType;
958 matches!(env.library.type_(inner_tid), Type::Basic(..) if ConversionType::of(env, inner_tid) == ConversionType::Direct)
959 }
960 _ => false,
961 }
962}
963
964fn analyze_async(
965 env: &Env,
966 func: &library::Function,
967 type_tid: library::TypeId,
968 codegen_name: &str,
969 callback_info: Option<CallbackInfo>,
970 commented: &mut bool,
971 trampoline: &mut Option<AsyncTrampoline>,
972 no_future: bool,
973 async_future: &mut Option<AsyncFuture>,
974 configured_functions: &[&config::functions::Function],
975 parameters: &function_parameters::Parameters,
976) -> bool {
977 if let Some(CallbackInfo {
978 callback_type,
979 success_parameters,
980 error_parameters,
981 bound_name,
982 }) = callback_info
983 {
984 *commented |= callback_type.contains("/*");
986 let func_name = &func.c_identifier;
987 let finish_func_name = if let Some(finish_func_name) = &func.finish_func {
988 finish_func_name.to_string()
989 } else {
990 finish_function_name(func_name)
991 };
992 let mut output_params = vec![];
993 let mut ffi_ret = None;
994 if let Some(function) = find_function(env, &finish_func_name) {
995 if use_function_return_for_result(
996 env,
997 function.ret.typ(),
998 &func.name,
999 configured_functions,
1000 ) {
1001 ffi_ret = Some(analysis::Parameter::from_return_value(
1002 env,
1003 function.ret.clone(),
1004 configured_functions,
1005 ));
1006 }
1007
1008 for param in &function.parameters {
1009 let mut lib_par = param.clone();
1010 if nameutil::needs_mangling(param.name()) {
1011 lib_par.set_name(&nameutil::mangle_keywords(param.name()));
1012 }
1013 let configured_parameters = configured_functions.matched_parameters(lib_par.name());
1014 output_params.push(analysis::Parameter::from_parameter(
1015 env,
1016 lib_par,
1017 &configured_parameters,
1018 ));
1019 }
1020 }
1021 if trampoline.is_some() || async_future.is_some() {
1022 warn_main!(
1023 type_tid,
1024 "{}: Cannot handle callbacks and async parameters at the same time for the \
1025 moment",
1026 func.name
1027 );
1028 *commented = true;
1029 return false;
1030 }
1031 if !*commented && success_parameters.is_empty() {
1032 if success_parameters.is_empty() {
1033 warn_main!(
1034 type_tid,
1035 "{}: missing success parameters for async future",
1036 func.name
1037 );
1038 }
1039 *commented = true;
1040 return false;
1041 }
1042 let is_method = func.kind == FunctionKind::Method;
1043
1044 *trampoline = Some(AsyncTrampoline {
1045 is_method,
1046 has_error_parameter: error_parameters.is_some(),
1047 name: format!("{codegen_name}_trampoline"),
1048 finish_func_name: format!("{}::{}", env.main_sys_crate_name(), finish_func_name),
1049 callback_type,
1050 bound_name,
1051 output_params,
1052 ffi_ret,
1053 });
1054
1055 if !no_future {
1056 *async_future = Some(AsyncFuture {
1057 is_method,
1058 name: format!("{}_future", codegen_name.trim_end_matches("_async")),
1059 success_parameters,
1060 error_parameters,
1061 assertion: match SafetyAssertionMode::of(env, is_method, parameters) {
1062 SafetyAssertionMode::None => SafetyAssertionMode::None,
1063 _ => SafetyAssertionMode::Skip,
1066 },
1067 });
1068 }
1069 true
1070 } else {
1071 false
1072 }
1073}
1074
1075fn analyze_callback(
1076 func_name: &str,
1077 type_tid: library::TypeId,
1078 env: &Env,
1079 par: &CParameter,
1080 callback_info: &Option<CallbackInfo>,
1081 commented: &mut bool,
1082 imports: &mut Imports,
1083 c_parameters: &[(&CParameter, usize)],
1084 rust_type: &Type,
1085 callback_parameters_config: Option<&config::functions::CallbackParameters>,
1086) -> Option<(Trampoline, Option<usize>)> {
1087 let mut imports_to_add = Vec::new();
1088
1089 if let Type::Function(func) = rust_type {
1090 if par.c_type != "GDestroyNotify" {
1091 if let Some(user_data) = par.user_data_index {
1092 if user_data >= c_parameters.len() {
1093 warn_main!(
1094 type_tid,
1095 "function `{}` has an invalid user data index of {} when there are {} parameters",
1096 func_name,
1097 user_data,
1098 c_parameters.len()
1099 );
1100 return None;
1101 } else if !is_gpointer(&c_parameters[user_data].0.c_type) {
1102 *commented = true;
1103 warn_main!(
1104 type_tid,
1105 "function `{}`'s callback `{}` has invalid user data",
1106 func_name,
1107 par.name
1108 );
1109 return None;
1110 }
1111 } else {
1112 *commented = true;
1113 warn_main!(
1114 type_tid,
1115 "function `{}`'s callback `{}` without associated user data",
1116 func_name,
1117 par.name
1118 );
1119 return None;
1120 }
1121 if let Some(destroy_index) = par.destroy_index {
1122 if destroy_index >= c_parameters.len() {
1123 warn_main!(
1124 type_tid,
1125 "function `{}` has an invalid destroy index of {} when there are {} \
1126 parameters",
1127 func_name,
1128 destroy_index,
1129 c_parameters.len()
1130 );
1131 return None;
1132 }
1133 if c_parameters[destroy_index].0.c_type != "GDestroyNotify" {
1134 *commented = true;
1135 warn_main!(
1136 type_tid,
1137 "function `{}`'s callback `{}` has invalid destroy callback",
1138 func_name,
1139 par.name
1140 );
1141 return None;
1142 }
1143 }
1144 }
1145
1146 if par.c_type != "GDestroyNotify"
1149 && (func.parameters.is_empty()
1150 || !func.parameters.iter().any(|c| c.closure().is_some()))
1151 {
1152 *commented = true;
1153 warn_main!(
1154 type_tid,
1155 "Closure type `{}` doesn't provide user data for function {}",
1156 par.c_type,
1157 func_name,
1158 );
1159 return None;
1160 }
1161
1162 let parameters = crate::analysis::trampoline_parameters::analyze(
1163 env,
1164 &func.parameters,
1165 par.typ,
1166 &[],
1167 callback_parameters_config,
1168 );
1169 if par.c_type != "GDestroyNotify" && !*commented {
1170 *commented |= func.parameters.iter().any(|p| {
1171 if p.closure().is_none() {
1172 crate::analysis::trampolines::type_error(env, p).is_some()
1173 } else {
1174 false
1175 }
1176 });
1177 }
1178 for p in ¶meters.rust_parameters {
1179 if let Ok(rust_type) = RustType::builder(env, p.typ)
1180 .direction(p.direction)
1181 .nullable(p.nullable)
1182 .try_from_glib(&p.try_from_glib)
1183 .try_build()
1184 {
1185 imports_to_add.extend(rust_type.into_used_types());
1186 }
1187 }
1188 if let Ok(rust_type) = RustType::builder(env, func.ret.typ())
1189 .direction(ParameterDirection::Return)
1190 .try_build()
1191 && !rust_type.as_str().ends_with("GString")
1192 && !rust_type.as_str().ends_with("GAsyncResult")
1193 {
1194 imports_to_add.extend(rust_type.into_used_types());
1195 }
1196 let user_data_index = par.user_data_index.unwrap_or(0);
1197 if par.c_type != "GDestroyNotify" && c_parameters.len() <= user_data_index {
1198 warn_main!(
1199 type_tid,
1200 "`{}`: Invalid user data index of `{}`",
1201 func.name,
1202 user_data_index
1203 );
1204 *commented = true;
1205 None
1206 } else if match par.destroy_index {
1207 Some(destroy_index) => c_parameters.len() <= destroy_index,
1208 None => false,
1209 } {
1210 warn_main!(
1211 type_tid,
1212 "`{}`: Invalid destroy index of `{}`",
1213 func.name,
1214 par.destroy_index.unwrap()
1215 );
1216 *commented = true;
1217 None
1218 } else {
1219 if !*commented {
1220 for import in imports_to_add {
1221 imports.add_used_type(&import);
1222 }
1223 }
1224 Some((
1225 Trampoline {
1226 name: par.name.to_string(),
1227 parameters,
1228 ret: func.ret.clone(),
1229 bound_name: match callback_info {
1230 Some(x) => x.bound_name.to_string(),
1231 None => match RustType::builder(env, par.typ)
1232 .direction(par.direction)
1233 .nullable(par.nullable)
1234 .scope(par.scope)
1235 .try_build()
1236 {
1237 Ok(rust_type) => rust_type.into_string(),
1238 Err(_) => {
1239 warn_main!(type_tid, "`{}`: unknown type", func.name);
1240 return None;
1241 }
1242 },
1243 },
1244 bounds: Bounds::default(),
1245 version: None,
1246 inhibit: false,
1247 concurrency: library::Concurrency::None,
1248 is_notify: false,
1249 scope: par.scope,
1250 user_data_index: if par.c_type != "GDestroyNotify" {
1252 c_parameters[user_data_index].1
1253 } else {
1254 0
1255 },
1256 destroy_index: 0,
1257 nullable: par.nullable,
1258 type_name: env.library.type_(type_tid).get_name(),
1259 },
1260 par.destroy_index
1261 .map(|destroy_index| c_parameters[destroy_index].1),
1262 ))
1263 }
1264 } else {
1265 None
1266 }
1267}
1268
1269pub fn find_function<'a>(env: &'a Env, c_identifier: &str) -> Option<&'a Function> {
1270 let find = |functions: &'a [Function]| -> Option<&'a Function> {
1271 functions
1272 .iter()
1273 .find(|&function| function.c_identifier == c_identifier)
1274 };
1275
1276 if let Some((index, _)) = env.library.find_namespace(&env.config.library_name) {
1277 let namespace = env.library.namespace(index);
1278 if let Some(f) = find(&namespace.functions) {
1279 return Some(f);
1280 }
1281 for typ in &namespace.types {
1282 if let Some(Type::Class(class)) = typ {
1283 if let Some(f) = find(&class.functions) {
1284 return Some(f);
1285 }
1286 } else if let Some(Type::Interface(interface)) = typ
1287 && let Some(f) = find(&interface.functions)
1288 {
1289 return Some(f);
1290 }
1291 }
1292 }
1293 None
1294}
1295
1296pub fn finish_function_name(mut func_name: &str) -> String {
1298 if func_name.ends_with("_async") {
1299 let len = func_name.len() - "_async".len();
1300 func_name = &func_name[0..len];
1301 }
1302 format!("{}_finish", &func_name)
1303}
1304
1305pub fn find_index_to_ignore<'a>(
1306 parameters: impl IntoIterator<Item = &'a library::Parameter>,
1307 ret: Option<&'a library::Parameter>,
1308) -> Option<usize> {
1309 parameters
1310 .into_iter()
1311 .chain(ret)
1312 .find(|param| param.array_length().is_some())
1313 .and_then(|param| param.array_length().map(|length| length as usize))
1314}
1315
1316#[cfg(test)]
1317mod tests {
1318 use super::*;
1319
1320 #[test]
1321 fn test_finish_function_name() {
1322 assert_eq!(
1323 "g_file_copy_finish",
1324 &finish_function_name("g_file_copy_async")
1325 );
1326 assert_eq!("g_bus_get_finish", &finish_function_name("g_bus_get"));
1327 }
1328}