From bfb0a2d016ebe0cb15c32c5f1cc0d3298c5b03bb Mon Sep 17 00:00:00 2001 From: Marijn Suijten Date: Wed, 25 Nov 2020 22:52:48 +0100 Subject: [PATCH 01/26] Fix clippy::search_is_some --- src/analysis/function_parameters.rs | 5 +---- src/analysis/functions.rs | 2 +- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/src/analysis/function_parameters.rs b/src/analysis/function_parameters.rs index 54c207a08..1572de778 100644 --- a/src/analysis/function_parameters.rs +++ b/src/analysis/function_parameters.rs @@ -375,11 +375,8 @@ fn is_length(par: &library::Parameter) -> bool { if len >= 3 && &par.name[len - 3..len] == "len" { return true; } - if par.name.find("length").is_some() { - return true; - } - false + par.name.contains("length") } fn has_length(env: &Env, typ: TypeId) -> bool { diff --git a/src/analysis/functions.rs b/src/analysis/functions.rs index 401a038b2..ff4843a86 100644 --- a/src/analysis/functions.rs +++ b/src/analysis/functions.rs @@ -845,7 +845,7 @@ fn analyze_async( }) = callback_info { // Checks for /*Ignored*/ or other error comments - *commented |= callback_type.find("/*").is_some(); + *commented |= callback_type.contains("/*"); let func_name = func.c_identifier.as_ref().unwrap(); let finish_func_name = finish_function_name(func_name); let mut output_params = vec![]; From a63936437a567b6b26a0b0c347f179508dcca09f Mon Sep 17 00:00:00 2001 From: Marijn Suijten Date: Wed, 25 Nov 2020 22:53:35 +0100 Subject: [PATCH 02/26] Fix clippy::unnecessary_wraps --- src/analysis/signals.rs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/analysis/signals.rs b/src/analysis/signals.rs index dd8bdfb58..84f0c43f3 100644 --- a/src/analysis/signals.rs +++ b/src/analysis/signals.rs @@ -47,9 +47,7 @@ pub fn analyze( obj, imports, ); - if let Some(info) = info { - sns.push(info); - } + sns.push(info); } sns @@ -63,7 +61,7 @@ fn analyze_signal( configured_signals: &[&config::signals::Signal], obj: &GObject, imports: &mut Imports, -) -> Option { +) -> Info { let mut used_types: Vec = Vec::with_capacity(4); let version = configured_signals .iter() @@ -117,5 +115,6 @@ fn analyze_signal( deprecated_version, doc_hidden, }; - Some(info) + + info } From e54e73f8974ec6a37fde6cc74a247e00096543c7 Mon Sep 17 00:00:00 2001 From: Marijn Suijten Date: Sun, 22 Nov 2020 22:06:28 +0100 Subject: [PATCH 03/26] env: Remove unnecessary `if` from main_sys_crate_name This function always returns the value from sys_crate_name. --- src/env.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/env.rs b/src/env.rs index 20a69ee70..26d6be55b 100644 --- a/src/env.rs +++ b/src/env.rs @@ -53,10 +53,6 @@ impl Env { } pub fn main_sys_crate_name(&self) -> &str { - if &self.namespaces[MAIN_NAMESPACE].sys_crate_name != "gobject_ffi" { - "ffi" - } else { - "gobject_ffi" - } + &self.namespaces[MAIN_NAMESPACE].sys_crate_name } } From 18d4a5468dbf21c7e1c75bb6e572c1ab6717d0bf Mon Sep 17 00:00:00 2001 From: Marijn Suijten Date: Mon, 30 Nov 2020 19:51:26 +0100 Subject: [PATCH 04/26] special_functions: Perform argument and ret validation on to_string --- src/analysis/special_functions.rs | 33 ++++++++++++++++++++++++------- 1 file changed, 26 insertions(+), 7 deletions(-) diff --git a/src/analysis/special_functions.rs b/src/analysis/special_functions.rs index 5849828b7..9c6e46f55 100644 --- a/src/analysis/special_functions.rs +++ b/src/analysis/special_functions.rs @@ -1,6 +1,9 @@ -use crate::analysis::{ - functions::{Info as FuncInfo, Visibility}, - imports::Imports, +use crate::{ + analysis::{ + functions::{Info as FuncInfo, Visibility}, + imports::Imports, + }, + library::TypeId, }; use std::{collections::BTreeMap, str::FromStr}; @@ -40,13 +43,30 @@ pub type Infos = BTreeMap; // Type => glib_name fn update_func(func: &mut FuncInfo, type_: Type) -> bool { if func.visibility != Visibility::Comment { - func.visibility = visibility(type_, func.parameters.c_parameters.len()); + func.visibility = visibility(type_); } - // I assume `to_string` functions never return `NULL` + if type_ == Type::ToString { + if func.parameters.c_parameters.len() != 1 { + return false; + } + if !func.parameters.c_parameters[0].instance_parameter { + return false; + } + if !func + .ret + .parameter + .as_ref() + .map_or(false, |p| p.typ == TypeId::tid_utf8()) + { + return false; + } + if let Some(par) = func.ret.parameter.as_mut() { + // I assume `to_string` functions never return `NULL` *par.nullable = false; } + if func.visibility != Visibility::Private { return false; } @@ -89,12 +109,11 @@ pub fn extract(functions: &mut Vec) -> Infos { specials } -fn visibility(t: Type, args_len: usize) -> Visibility { +fn visibility(t: Type) -> Visibility { use self::Type::*; match t { Copy | Free | Ref | Unref => Visibility::Hidden, Hash | Compare | Equal => Visibility::Private, - ToString if args_len == 1 => Visibility::Private, ToString => Visibility::Public, } } From 68e4c73cf79cb279a5d03695739d2d0fd6c26b0b Mon Sep 17 00:00:00 2001 From: Marijn Suijten Date: Mon, 30 Nov 2020 20:09:20 +0100 Subject: [PATCH 05/26] special_functions: Rename ToString after the trait it impls: Display --- src/analysis/object.rs | 2 +- src/analysis/special_functions.rs | 10 +++++----- src/codegen/trait_impls.rs | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/analysis/object.rs b/src/analysis/object.rs index 45cb59b2b..25ed37ea5 100644 --- a/src/analysis/object.rs +++ b/src/analysis/object.rs @@ -98,7 +98,7 @@ pub fn class(env: &Env, obj: &GObject, deps: &[library::TypeId]) -> Option special_functions::Type::Hash, special_functions::Type::Equal, special_functions::Type::Compare, - special_functions::Type::ToString, + special_functions::Type::Display, ] { special_functions::unhide(&mut functions, &specials, *t); specials.remove(t); diff --git a/src/analysis/special_functions.rs b/src/analysis/special_functions.rs index 9c6e46f55..28eba95de 100644 --- a/src/analysis/special_functions.rs +++ b/src/analysis/special_functions.rs @@ -14,7 +14,7 @@ pub enum Type { Equal, Free, Ref, - ToString, + Display, Unref, Hash, } @@ -31,7 +31,7 @@ impl FromStr for Type { "free" | "destroy" => Ok(Free), "is_equal" => Ok(Equal), "ref" | "ref_" => Ok(Ref), - "to_string" => Ok(ToString), + "to_string" => Ok(Display), "unref" => Ok(Unref), "hash" => Ok(Hash), _ => Err(format!("Unknown type '{}'", s)), @@ -46,7 +46,7 @@ fn update_func(func: &mut FuncInfo, type_: Type) -> bool { func.visibility = visibility(type_); } - if type_ == Type::ToString { + if type_ == Type::Display { if func.parameters.c_parameters.len() != 1 { return false; } @@ -114,7 +114,7 @@ fn visibility(t: Type) -> Visibility { match t { Copy | Free | Ref | Unref => Visibility::Hidden, Hash | Compare | Equal => Visibility::Private, - ToString => Visibility::Public, + Display => Visibility::Public, } } @@ -135,7 +135,7 @@ pub fn analyze_imports(specials: &Infos, imports: &mut Imports) { for type_ in specials.keys() { match *type_ { Compare => imports.add("std::cmp"), - ToString => imports.add("std::fmt"), + Display => imports.add("std::fmt"), Hash => imports.add("std::hash"), _ => {} } diff --git a/src/codegen/trait_impls.rs b/src/codegen/trait_impls.rs index 7eb81ba61..2bd51a819 100644 --- a/src/codegen/trait_impls.rs +++ b/src/codegen/trait_impls.rs @@ -28,7 +28,7 @@ pub fn generate( Type::Equal => { generate_eq(w, env, type_name, info, trait_name)?; } - Type::ToString => generate_display(w, env, type_name, info, trait_name)?, + Type::Display => generate_display(w, env, type_name, info, trait_name)?, Type::Hash => generate_hash(w, env, type_name, info, trait_name)?, _ => {} } From 0f554d7d0d95f78fffc267075836f957b6f31bd4 Mon Sep 17 00:00:00 2001 From: Marijn Suijten Date: Sun, 15 Nov 2020 18:03:38 +0100 Subject: [PATCH 06/26] codegen/parameter: Do not pass ByRef::None as reference --- src/codegen/parameter.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/codegen/parameter.rs b/src/codegen/parameter.rs index 2744b157e..a1506ef8a 100644 --- a/src/codegen/parameter.rs +++ b/src/codegen/parameter.rs @@ -16,22 +16,22 @@ pub trait ToParameter { impl ToParameter for CParameter { fn to_parameter(&self, env: &Env, bounds: &Bounds) -> String { - let mut_str = if self.ref_mode == RefMode::ByRefMut { - "mut " - } else { - "" + let ref_str = match self.ref_mode { + RefMode::ByRefMut => "&mut ", + RefMode::None => "", + _ => "&", }; if self.instance_parameter { - format!("&{}self", mut_str) + format!("{}self", ref_str) } else { let type_str: String; match bounds.get_parameter_alias_info(&self.name) { Some((t, bound_type)) => match bound_type { BoundType::NoWrapper => type_str = t.to_string(), BoundType::IsA(_) if *self.nullable => { - type_str = format!("Option<&{}{}>", mut_str, t) + type_str = format!("Option<{}{}>", ref_str, t) } - BoundType::IsA(_) => type_str = format!("&{}{}", mut_str, t), + BoundType::IsA(_) => type_str = format!("{}{}", ref_str, t), BoundType::AsRef(_) => type_str = t.to_string(), }, None => { From 354c580230785e73e9658ad35f21e538271961b4 Mon Sep 17 00:00:00 2001 From: Marijn Suijten Date: Sat, 5 Dec 2020 23:10:08 +0100 Subject: [PATCH 07/26] Remove unnecessary main_sys_crate_name imports `crate::ffi`, which these calls resolve to are ignored in Imports::common_checks, no need to pass these explicitly. --- src/analysis/imports.rs | 7 +++++-- src/analysis/mod.rs | 1 - src/analysis/object.rs | 2 -- src/analysis/record.rs | 1 - src/codegen/constants.rs | 1 - src/codegen/enums.rs | 1 - src/codegen/flags.rs | 1 - 7 files changed, 5 insertions(+), 9 deletions(-) diff --git a/src/analysis/imports.rs b/src/analysis/imports.rs index 87be8c08c..1d24ebca2 100644 --- a/src/analysis/imports.rs +++ b/src/analysis/imports.rs @@ -120,11 +120,14 @@ impl Imports { self.defaults.clear(); } - /// The goals of this function is to discard unwanted imports like "ffi" and "crate". It + /// The goals of this function is to discard unwanted imports like "crate". It /// also extends the checks in case you are implementing "X". For example, you don't want to /// import "X" or "crate::X" in this case. fn common_checks(&self, name: &str) -> bool { - if name == "crate::ffi" || (!name.contains("::") && name != "xlib") { + // The ffi namespace is used directly, including it is a programmer error. + assert_ne!(name, "crate::ffi"); + + if !name.contains("::") && name != "xlib" { false } else if let Some(ref defined) = self.defined { !((name.starts_with("crate::") && &name[7..] == defined) || name == defined) diff --git a/src/analysis/mod.rs b/src/analysis/mod.rs index 512fac459..609bba2be 100644 --- a/src/analysis/mod.rs +++ b/src/analysis/mod.rs @@ -110,7 +110,6 @@ fn analyze_global_functions(env: &mut Env) { let mut imports = imports::Imports::new(&env.library); imports.add("glib::translate::*"); - imports.add(&format!("crate::{}", env.main_sys_crate_name())); let functions = functions::analyze( env, diff --git a/src/analysis/object.rs b/src/analysis/object.rs index 25ed37ea5..b21e11f20 100644 --- a/src/analysis/object.rs +++ b/src/analysis/object.rs @@ -62,7 +62,6 @@ pub fn class(env: &Env, obj: &GObject, deps: &[library::TypeId]) -> Option let mut imports = Imports::with_defined(&env.library, &name); imports.add("glib::translate::*"); - imports.add(&format!("crate::{}", env.main_sys_crate_name())); if obj.generate_display_trait { imports.add("std::fmt"); } @@ -230,7 +229,6 @@ pub fn interface(env: &Env, obj: &GObject, deps: &[library::TypeId]) -> Option Option { let is_boxed = obj.use_boxed_functions || RecordType::of(&record) == RecordType::AutoBoxed; let mut imports = Imports::with_defined(&env.library, &name); - imports.add(&format!("crate::{}", env.main_sys_crate_name())); let mut functions = functions::analyze( env, diff --git a/src/codegen/constants.rs b/src/codegen/constants.rs index 7a63ade34..9a6ba6db4 100644 --- a/src/codegen/constants.rs +++ b/src/codegen/constants.rs @@ -17,7 +17,6 @@ pub fn generate(env: &Env, root_path: &Path, mod_rs: &mut Vec) { } let sys_crate_name = env.main_sys_crate_name(); - imports.add(&format!("crate::{}", sys_crate_name)); imports.add("std::ffi::CStr"); file_saver::save_to_file(path, env.config.make_backup, |w| { diff --git a/src/codegen/enums.rs b/src/codegen/enums.rs index 2e03c77e4..8f2f2bb00 100644 --- a/src/codegen/enums.rs +++ b/src/codegen/enums.rs @@ -53,7 +53,6 @@ pub fn generate(env: &Env, root_path: &Path, mod_rs: &mut Vec) { } let mut imports = Imports::new(&env.library); - imports.add(&format!("crate::{}", env.main_sys_crate_name())); if has_get_quark { imports.add("glib::Quark"); imports.add("glib::error::ErrorDomain"); diff --git a/src/codegen/flags.rs b/src/codegen/flags.rs index fb6b8a066..a6240d5e6 100644 --- a/src/codegen/flags.rs +++ b/src/codegen/flags.rs @@ -35,7 +35,6 @@ pub fn generate(env: &Env, root_path: &Path, mod_rs: &mut Vec) { let path = root_path.join("flags.rs"); file_saver::save_to_file(path, env.config.make_backup, |w| { let mut imports = Imports::new(&env.library); - imports.add(&format!("crate::{}", env.main_sys_crate_name())); imports.add("glib::translate::*"); imports.add("bitflags::bitflags"); From ef8c22be36fe2fa55dec1939a269b8d15f34c2fd Mon Sep 17 00:00:00 2001 From: Marijn Suijten Date: Sun, 30 Aug 2020 01:16:18 +0200 Subject: [PATCH 08/26] Generate associated functions for enums --- src/analysis/enums.rs | 79 +++++++++++++++++++++++++++++++++++++++++++ src/analysis/mod.rs | 7 ++++ src/codegen/enums.rs | 72 ++++++++++++++++++++++++--------------- 3 files changed, 130 insertions(+), 28 deletions(-) create mode 100644 src/analysis/enums.rs diff --git a/src/analysis/enums.rs b/src/analysis/enums.rs new file mode 100644 index 000000000..52b3f604d --- /dev/null +++ b/src/analysis/enums.rs @@ -0,0 +1,79 @@ +use super::{imports::Imports, info_base::InfoBase, *}; +use crate::{config::gobjects::GObject, env::Env, library, nameutil::*, traits::*}; +use log::info; +use std::ops::Deref; + +#[derive(Debug, Default)] +pub struct Info { + pub base: InfoBase, +} + +impl Deref for Info { + type Target = InfoBase; + + fn deref(&self) -> &InfoBase { + &self.base + } +} + +impl Info { + pub fn type_<'a>(&self, library: &'a library::Library) -> &'a library::Enumeration { + let type_ = library + .type_(self.type_id) + .maybe_ref() + .unwrap_or_else(|| panic!("{} is not an enumeration.", self.full_name)); + type_ + } +} + +pub fn new(env: &Env, obj: &GObject) -> Option { + info!("Analyzing enumeration {}", obj.name); + + let enumeration_tid = env.library.find_type(0, &obj.name)?; + let type_ = env.type_(enumeration_tid); + let enumeration: &library::Enumeration = type_.maybe_ref()?; + + let name = split_namespace_name(&obj.name).1; + + let mut imports = Imports::with_defined(&env.library, name); + + let mut functions = functions::analyze( + env, + &enumeration.functions, + enumeration_tid, + false, + false, + obj, + &mut imports, + None, + None, + ); + let specials = special_functions::extract(&mut functions); + + special_functions::analyze_imports(&specials, &mut imports); + + let (version, deprecated_version) = info_base::versions( + env, + obj, + &functions, + enumeration.version, + enumeration.deprecated_version, + ); + + let base = InfoBase { + full_name: obj.name.clone(), + type_id: enumeration_tid, + name: name.to_owned(), + functions, + specials, + imports, + version, + deprecated_version, + cfg_condition: obj.cfg_condition.clone(), + concurrency: obj.concurrency, + }; + + let info = Info { base }; + + Some(info) +} diff --git a/src/analysis/mod.rs b/src/analysis/mod.rs index 609bba2be..831e77f9a 100644 --- a/src/analysis/mod.rs +++ b/src/analysis/mod.rs @@ -12,6 +12,7 @@ pub mod class_builder; pub mod class_hierarchy; pub mod constants; pub mod conversion_type; +pub mod enums; pub mod ffi_type; pub mod function_parameters; pub mod functions; @@ -44,6 +45,7 @@ pub struct Analysis { pub records: BTreeMap, pub global_functions: Option, pub constants: Vec, + pub enumerations: Vec, } pub fn run(env: &mut Env) { @@ -173,6 +175,11 @@ fn analyze(env: &mut Env, tid: TypeId, deps: &[TypeId]) { env.analysis.records.insert(full_name, info); } } + Type::Enumeration(_) => { + if let Some(info) = enums::new(env, obj) { + env.analysis.enumerations.push(info); + } + } _ => {} } } diff --git a/src/codegen/enums.rs b/src/codegen/enums.rs index 8f2f2bb00..66af210df 100644 --- a/src/codegen/enums.rs +++ b/src/codegen/enums.rs @@ -1,4 +1,6 @@ +use super::function; use crate::{ + analysis::enums::Info, analysis::{imports::Imports, namespaces}, codegen::general::{ self, cfg_deprecated, derives, version_condition, version_condition_no_doc, @@ -19,32 +21,33 @@ use std::{ }; pub fn generate(env: &Env, root_path: &Path, mod_rs: &mut Vec) { - let configs: Vec<&GObject> = env - .config - .objects - .values() - .filter(|c| { - c.status.need_generate() && c.type_id.map_or(false, |tid| tid.ns_id == namespaces::MAIN) - }) - .collect(); + fn should_keep(c: &GObject) -> bool { + c.status.need_generate() && c.type_id.map_or(false, |tid| tid.ns_id == namespaces::MAIN) + } + let mut has_get_quark = false; let mut has_any = false; let mut has_get_type = false; let mut generate_display_trait = false; - for config in &configs { - if let Type::Enumeration(ref enum_) = *env.library.type_(config.type_id.unwrap()) { - has_any = true; - if enum_.error_domain.is_some() { - has_get_quark = true; - } - if enum_.glib_get_type.is_some() { - has_get_type = true; - } - generate_display_trait |= config.generate_display_trait; - if has_get_type && has_get_quark { - break; - } + for enum_analysis in &env.analysis.enumerations { + let config = &env.config.objects[&enum_analysis.full_name]; + if !should_keep(config) { + continue; + } + + let enum_ = enum_analysis.type_(&env.library); + has_any = true; + if enum_.error_domain.is_some() { + has_get_quark = true; + } + if enum_.glib_get_type.is_some() { + has_get_type = true; + } + generate_display_trait |= config.generate_display_trait; + + if has_get_type && has_get_quark { + break; } } @@ -77,14 +80,18 @@ pub fn generate(env: &Env, root_path: &Path, mod_rs: &mut Vec) { writeln!(w)?; mod_rs.push("\nmod enums;".into()); - for config in &configs { - if let Type::Enumeration(ref enum_) = *env.library.type_(config.type_id.unwrap()) { - if let Some(cfg) = version_condition_string(env, enum_.version, false, 0) { - mod_rs.push(cfg); - } - mod_rs.push(format!("pub use self::enums::{};", enum_.name)); - generate_enum(env, w, enum_, config)?; + for enum_analysis in &env.analysis.enumerations { + let config = &env.config.objects[&enum_analysis.full_name]; + if !should_keep(config) { + continue; + } + let enum_ = enum_analysis.type_(&env.library); + + if let Some(cfg) = version_condition_string(env, enum_.version, false, 0) { + mod_rs.push(cfg); } + mod_rs.push(format!("pub use self::enums::{};", enum_.name)); + generate_enum(env, w, enum_, config, enum_analysis)?; } Ok(()) @@ -97,6 +104,7 @@ fn generate_enum( w: &mut dyn Write, enum_: &Enumeration, config: &GObject, + analysis: &Info, ) -> Result<()> { struct Member { name: String, @@ -161,6 +169,14 @@ fn generate_enum( " )?; + version_condition(w, env, enum_.version, false, 0)?; + write!(w, "impl {} {{", analysis.name)?; + for func_analysis in &analysis.functions() { + function::generate(w, env, func_analysis, false, false, 1)?; + } + writeln!(w, "}}")?; + writeln!(w)?; + if config.generate_display_trait { // Generate Display trait implementation. version_condition(w, env, enum_.version, false, 0)?; From 9bc5f6e9687256f41dbaeda3630f6d15e6c5474d Mon Sep 17 00:00:00 2001 From: Marijn Suijten Date: Sun, 30 Aug 2020 13:05:38 +0200 Subject: [PATCH 09/26] enum: all imports should go to one Imports as it's one file --- src/analysis/enums.rs | 11 +++++------ src/analysis/mod.rs | 14 +++++++++++--- src/codegen/enums.rs | 6 ++++-- 3 files changed, 20 insertions(+), 11 deletions(-) diff --git a/src/analysis/enums.rs b/src/analysis/enums.rs index 52b3f604d..578a4d925 100644 --- a/src/analysis/enums.rs +++ b/src/analysis/enums.rs @@ -26,7 +26,7 @@ impl Info { } } -pub fn new(env: &Env, obj: &GObject) -> Option { +pub fn new(env: &Env, obj: &GObject, imports: &mut Imports) -> Option { info!("Analyzing enumeration {}", obj.name); let enumeration_tid = env.library.find_type(0, &obj.name)?; @@ -35,8 +35,6 @@ pub fn new(env: &Env, obj: &GObject) -> Option { let name = split_namespace_name(&obj.name).1; - let mut imports = Imports::with_defined(&env.library, name); - let mut functions = functions::analyze( env, &enumeration.functions, @@ -44,13 +42,13 @@ pub fn new(env: &Env, obj: &GObject) -> Option { false, false, obj, - &mut imports, + imports, None, None, ); let specials = special_functions::extract(&mut functions); - special_functions::analyze_imports(&specials, &mut imports); + special_functions::analyze_imports(&specials, imports); let (version, deprecated_version) = info_base::versions( env, @@ -66,7 +64,8 @@ pub fn new(env: &Env, obj: &GObject) -> Option { name: name.to_owned(), functions, specials, - imports, + // TODO: Don't use! + imports: Imports::new(&env.library), version, deprecated_version, cfg_condition: obj.cfg_condition.clone(), diff --git a/src/analysis/mod.rs b/src/analysis/mod.rs index 831e77f9a..febc978a1 100644 --- a/src/analysis/mod.rs +++ b/src/analysis/mod.rs @@ -2,6 +2,7 @@ use crate::{ env::Env, library::{self, Type, TypeId}, }; +use imports::Imports; use log::error; use std::collections::BTreeMap; @@ -46,6 +47,7 @@ pub struct Analysis { pub global_functions: Option, pub constants: Vec, pub enumerations: Vec, + pub enum_imports: Imports, } pub fn run(env: &mut Env) { @@ -62,6 +64,8 @@ pub fn run(env: &mut Env) { to_analyze.push((tid, deps)); } + let mut enum_imports = Imports::new(&env.library); + let mut analyzed = 1; while analyzed > 0 { analyzed = 0; @@ -71,12 +75,15 @@ pub fn run(env: &mut Env) { new_to_analyze.push((tid, deps.clone())); continue; } - analyze(env, tid, deps); + analyze(env, tid, deps, &mut enum_imports); analyzed += 1; } to_analyze = new_to_analyze; } + + env.analysis.enum_imports = enum_imports; + if !to_analyze.is_empty() { error!( "Not analyzed {} objects due unfinished dependencies", @@ -153,7 +160,7 @@ fn analyze_constants(env: &mut Env) { env.analysis.constants = constants::analyze(env, &constants, obj); } -fn analyze(env: &mut Env, tid: TypeId, deps: &[TypeId]) { +fn analyze(env: &mut Env, tid: TypeId, deps: &[TypeId], enum_imports: &mut Imports) { let full_name = tid.full_name(&env.library); let obj = match env.config.objects.get(&*full_name) { Some(obj) => obj, @@ -176,7 +183,8 @@ fn analyze(env: &mut Env, tid: TypeId, deps: &[TypeId]) { } } Type::Enumeration(_) => { - if let Some(info) = enums::new(env, obj) { + // Cannot mutably borrow env.analysis.enum_imports here + if let Some(info) = enums::new(env, obj, enum_imports) { env.analysis.enumerations.push(info); } } diff --git a/src/codegen/enums.rs b/src/codegen/enums.rs index 66af210df..717a58af0 100644 --- a/src/codegen/enums.rs +++ b/src/codegen/enums.rs @@ -1,7 +1,7 @@ use super::function; use crate::{ analysis::enums::Info, - analysis::{imports::Imports, namespaces}, + analysis::namespaces, codegen::general::{ self, cfg_deprecated, derives, version_condition, version_condition_no_doc, version_condition_string, @@ -55,7 +55,9 @@ pub fn generate(env: &Env, root_path: &Path, mod_rs: &mut Vec) { return; } - let mut imports = Imports::new(&env.library); + // TODO: We should do this just once in analysis without mutation necessary afterwards + let mut imports = env.analysis.enum_imports.clone(); + if has_get_quark { imports.add("glib::Quark"); imports.add("glib::error::ErrorDomain"); From 841582f7c03277a5d3d350b177d6b50634cf7979 Mon Sep 17 00:00:00 2001 From: Marijn Suijten Date: Fri, 11 Sep 2020 21:04:59 +0200 Subject: [PATCH 10/26] Only generate enum functions if >=1 function --- src/codegen/enums.rs | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/codegen/enums.rs b/src/codegen/enums.rs index 717a58af0..2b9ee2df0 100644 --- a/src/codegen/enums.rs +++ b/src/codegen/enums.rs @@ -167,16 +167,21 @@ fn generate_enum( "\ #[doc(hidden)] __Unknown(i32), -}} -" +}}" )?; - version_condition(w, env, enum_.version, false, 0)?; - write!(w, "impl {} {{", analysis.name)?; - for func_analysis in &analysis.functions() { - function::generate(w, env, func_analysis, false, false, 1)?; + let functions = analysis.functions(); + + if !functions.is_empty() { + writeln!(w)?; + version_condition(w, env, enum_.version, false, 0)?; + write!(w, "impl {} {{", analysis.name)?; + for func_analysis in functions { + function::generate(w, env, func_analysis, false, false, 1)?; + } + writeln!(w, "}}")?; } - writeln!(w, "}}")?; + writeln!(w)?; if config.generate_display_trait { From 6e89f569bf7fde2319be1e58705bcd765488e0d3 Mon Sep 17 00:00:00 2001 From: Marijn Suijten Date: Sat, 14 Nov 2020 18:16:25 +0100 Subject: [PATCH 11/26] imports: Add support for multiple, lazily added declared symbols This is used in enum function generation where all enum types asssociated with a given crate are defined in the same file. --- src/analysis/enums.rs | 3 +++ src/analysis/imports.rs | 27 ++++++++++++++++++--------- 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/src/analysis/enums.rs b/src/analysis/enums.rs index 578a4d925..c79670d8b 100644 --- a/src/analysis/enums.rs +++ b/src/analysis/enums.rs @@ -35,6 +35,9 @@ pub fn new(env: &Env, obj: &GObject, imports: &mut Imports) -> Option { let name = split_namespace_name(&obj.name).1; + // Mark the type as available within the enum namespace: + imports.add_defined(&format!("crate::{}", name)); + let mut functions = functions::analyze( env, &enumeration.functions, diff --git a/src/analysis/imports.rs b/src/analysis/imports.rs index 1d24ebca2..6b2cc63e1 100644 --- a/src/analysis/imports.rs +++ b/src/analysis/imports.rs @@ -3,6 +3,7 @@ use crate::{library::Library, nameutil::crate_name, version::Version}; use std::borrow::Cow; use std::cmp::Ordering; use std::collections::btree_map::BTreeMap; +use std::collections::HashSet; use std::ops::{Deref, DerefMut}; use std::vec::IntoIter; @@ -70,10 +71,8 @@ fn compare_imports(a: &(&String, &ImportConditions), b: &(&String, &ImportCondit pub struct Imports { /// Name of the current crate. crate_name: String, - /// Name defined within current module. It doesn't need use declaration. - /// - /// NOTE: Currently we don't need to support more than one such name. - defined: Option, + /// Names defined within current module. It doesn't need use declaration. + defined: HashSet, defaults: ImportConditions, map: BTreeMap, } @@ -82,7 +81,7 @@ impl Imports { pub fn new(gir: &Library) -> Imports { Imports { crate_name: make_crate_name(gir), - defined: None, + defined: HashSet::new(), defaults: ImportConditions::default(), map: BTreeMap::new(), } @@ -91,7 +90,7 @@ impl Imports { pub fn with_defined(gir: &Library, name: &str) -> Imports { Imports { crate_name: make_crate_name(gir), - defined: Some(name.to_owned()), + defined: std::iter::once(name.to_owned()).collect(), defaults: ImportConditions::default(), map: BTreeMap::new(), } @@ -127,15 +126,25 @@ impl Imports { // The ffi namespace is used directly, including it is a programmer error. assert_ne!(name, "crate::ffi"); - if !name.contains("::") && name != "xlib" { + if (!name.contains("::") && name != "xlib") || self.defined.contains(name) { false - } else if let Some(ref defined) = self.defined { - !((name.starts_with("crate::") && &name[7..] == defined) || name == defined) + } else if let Some(name) = name.strip_prefix("crate::") { + !self.defined.contains(name) } else { true } } + /// Declares that `name` is defined in scope + /// + /// Removes existing imports from `self.map` and marks `name` as + /// available to counter future import "requests". + pub fn add_defined(&mut self, name: &str) { + if self.defined.insert(name.to_owned()) { + self.map.remove(name); + } + } + /// Declares that name should be available through its last path component. /// /// For example, if name is `X::Y::Z` then it will be available as `Z`. From 50cee9e04e3b1681beefe37abb47032816120742 Mon Sep 17 00:00:00 2001 From: Marijn Suijten Date: Sun, 15 Nov 2020 00:17:42 +0100 Subject: [PATCH 12/26] enums: Move rejection and glib imports from codegen to analysis This not only cleans up unnecessary mutable variables in the codegen loop and a cloned Imports instance (so that it can remain immutable during codegen), it also adds the missing rejection logic to enum analysis. No harm was caused because these checks happened at codegen prior, but they omit some unnecessary imports. --- src/analysis/enums.rs | 30 ++++++++++++++++++++++ src/analysis/mod.rs | 1 + src/codegen/enums.rs | 58 ++----------------------------------------- 3 files changed, 33 insertions(+), 56 deletions(-) diff --git a/src/analysis/enums.rs b/src/analysis/enums.rs index c79670d8b..e05950f64 100644 --- a/src/analysis/enums.rs +++ b/src/analysis/enums.rs @@ -29,6 +29,17 @@ impl Info { pub fn new(env: &Env, obj: &GObject, imports: &mut Imports) -> Option { info!("Analyzing enumeration {}", obj.name); + if !obj.status.need_generate() { + return None; + } + + if !obj + .type_id + .map_or(false, |tid| tid.ns_id == namespaces::MAIN) + { + return None; + } + let enumeration_tid = env.library.find_type(0, &obj.name)?; let type_ = env.type_(enumeration_tid); let enumeration: &library::Enumeration = type_.maybe_ref()?; @@ -38,6 +49,25 @@ pub fn new(env: &Env, obj: &GObject, imports: &mut Imports) -> Option { // Mark the type as available within the enum namespace: imports.add_defined(&format!("crate::{}", name)); + let has_get_quark = enumeration.error_domain.is_some(); + if has_get_quark { + imports.add("glib::Quark"); + imports.add("glib::error::ErrorDomain"); + } + + let has_get_type = enumeration.glib_get_type.is_some(); + if has_get_type { + imports.add("glib::Type"); + imports.add("glib::StaticType"); + imports.add("glib::value::SetValue"); + imports.add("glib::value::FromValue"); + imports.add("glib::value::FromValueOptional"); + } + + if obj.generate_display_trait { + imports.add("std::fmt"); + } + let mut functions = functions::analyze( env, &enumeration.functions, diff --git a/src/analysis/mod.rs b/src/analysis/mod.rs index febc978a1..31cf7b119 100644 --- a/src/analysis/mod.rs +++ b/src/analysis/mod.rs @@ -65,6 +65,7 @@ pub fn run(env: &mut Env) { } let mut enum_imports = Imports::new(&env.library); + enum_imports.add("glib::translate::*"); let mut analyzed = 1; while analyzed > 0 { diff --git a/src/codegen/enums.rs b/src/codegen/enums.rs index 2b9ee2df0..4e8314b27 100644 --- a/src/codegen/enums.rs +++ b/src/codegen/enums.rs @@ -1,7 +1,6 @@ use super::function; use crate::{ analysis::enums::Info, - analysis::namespaces, codegen::general::{ self, cfg_deprecated, derives, version_condition, version_condition_no_doc, version_condition_string, @@ -21,72 +20,19 @@ use std::{ }; pub fn generate(env: &Env, root_path: &Path, mod_rs: &mut Vec) { - fn should_keep(c: &GObject) -> bool { - c.status.need_generate() && c.type_id.map_or(false, |tid| tid.ns_id == namespaces::MAIN) - } - - let mut has_get_quark = false; - let mut has_any = false; - let mut has_get_type = false; - let mut generate_display_trait = false; - - for enum_analysis in &env.analysis.enumerations { - let config = &env.config.objects[&enum_analysis.full_name]; - if !should_keep(config) { - continue; - } - - let enum_ = enum_analysis.type_(&env.library); - has_any = true; - if enum_.error_domain.is_some() { - has_get_quark = true; - } - if enum_.glib_get_type.is_some() { - has_get_type = true; - } - generate_display_trait |= config.generate_display_trait; - - if has_get_type && has_get_quark { - break; - } - } - - if !has_any { + if env.analysis.enumerations.is_empty() { return; } - // TODO: We should do this just once in analysis without mutation necessary afterwards - let mut imports = env.analysis.enum_imports.clone(); - - if has_get_quark { - imports.add("glib::Quark"); - imports.add("glib::error::ErrorDomain"); - } - if has_get_type { - imports.add("glib::Type"); - imports.add("glib::StaticType"); - imports.add("glib::value::SetValue"); - imports.add("glib::value::FromValue"); - imports.add("glib::value::FromValueOptional"); - } - imports.add("glib::translate::*"); - - if generate_display_trait { - imports.add("std::fmt"); - } - let path = root_path.join("enums.rs"); file_saver::save_to_file(path, env.config.make_backup, |w| { general::start_comments(w, &env.config)?; - general::uses(w, env, &imports)?; + general::uses(w, env, &env.analysis.enum_imports)?; writeln!(w)?; mod_rs.push("\nmod enums;".into()); for enum_analysis in &env.analysis.enumerations { let config = &env.config.objects[&enum_analysis.full_name]; - if !should_keep(config) { - continue; - } let enum_ = enum_analysis.type_(&env.library); if let Some(cfg) = version_condition_string(env, enum_.version, false, 0) { From 70eab37b826d47d5bdce7be70f4b10bd8d5f1c82 Mon Sep 17 00:00:00 2001 From: Marijn Suijten Date: Sun, 15 Nov 2020 16:35:04 +0100 Subject: [PATCH 13/26] codegen/enums: Add missing trait_impls::generate call This function is responsible for generating a Display impl, and caused unused std::fmt import previously. --- src/codegen/enums.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/codegen/enums.rs b/src/codegen/enums.rs index 4e8314b27..5c3dfe3b4 100644 --- a/src/codegen/enums.rs +++ b/src/codegen/enums.rs @@ -1,4 +1,4 @@ -use super::function; +use super::{function, trait_impls}; use crate::{ analysis::enums::Info, codegen::general::{ @@ -128,6 +128,14 @@ fn generate_enum( writeln!(w, "}}")?; } + trait_impls::generate( + w, + &analysis.name, + &analysis.functions, + &analysis.specials, + None, + )?; + writeln!(w)?; if config.generate_display_trait { From 3023445e0600620e7a82ffba74a57c256d4556e4 Mon Sep 17 00:00:00 2001 From: Marijn Suijten Date: Sun, 15 Nov 2020 18:02:45 +0100 Subject: [PATCH 14/26] analysis/enums: Treat first function parameter as instance --- src/analysis/enums.rs | 35 ++++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/src/analysis/enums.rs b/src/analysis/enums.rs index e05950f64..9ed259c7c 100644 --- a/src/analysis/enums.rs +++ b/src/analysis/enums.rs @@ -1,5 +1,6 @@ -use super::{imports::Imports, info_base::InfoBase, *}; +use super::{function_parameters::TransformationType, imports::Imports, info_base::InfoBase, *}; use crate::{config::gobjects::GObject, env::Env, library, nameutil::*, traits::*}; + use log::info; use std::ops::Deref; @@ -79,6 +80,38 @@ pub fn new(env: &Env, obj: &GObject, imports: &mut Imports) -> Option { None, None, ); + + // Gir does not currently mark the first parameter of associated enum functions - + // that are identical to its enum type - as instance parameter since most languages + // do not support this. + for f in &mut functions { + if f.parameters.c_parameters.is_empty() { + continue; + } + + let first_param = &mut f.parameters.c_parameters[0]; + + if first_param.typ == enumeration_tid { + first_param.instance_parameter = true; + + let t = f + .parameters + .transformations + .iter_mut() + .find(|t| t.ind_c == 0) + .unwrap(); + + if let TransformationType::ToGlibScalar { name, .. } = &mut t.transformation_type { + *name = "self".to_owned(); + } else { + panic!( + "Enumeration function instance param must be passed as scalar, not {:?}", + t.transformation_type + ); + } + } + } + let specials = special_functions::extract(&mut functions); special_functions::analyze_imports(&specials, imports); From 71232805c4735d0cd24949e44fac2b8e06aec4a8 Mon Sep 17 00:00:00 2001 From: Marijn Suijten Date: Mon, 16 Nov 2020 00:36:22 +0100 Subject: [PATCH 15/26] codegen: Add version condition on special function traits --- src/codegen/enums.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/codegen/enums.rs b/src/codegen/enums.rs index 5c3dfe3b4..f429fccb0 100644 --- a/src/codegen/enums.rs +++ b/src/codegen/enums.rs @@ -130,6 +130,7 @@ fn generate_enum( trait_impls::generate( w, + env, &analysis.name, &analysis.functions, &analysis.specials, From c3e827f83aedea77c6f950f9c32875bc1f5cf423 Mon Sep 17 00:00:00 2001 From: Marijn Suijten Date: Wed, 18 Nov 2020 22:55:29 +0100 Subject: [PATCH 16/26] enums: Generate special to_str for static functions --- src/analysis/enums.rs | 4 +- src/analysis/object.rs | 3 +- src/analysis/record.rs | 2 +- src/analysis/special_functions.rs | 98 +++++++++++++++++++++++++------ src/codegen/enums.rs | 11 +++- src/codegen/function.rs | 36 ++++++++++++ src/codegen/record.rs | 8 +-- src/codegen/trait_impls.rs | 6 +- 8 files changed, 137 insertions(+), 31 deletions(-) diff --git a/src/analysis/enums.rs b/src/analysis/enums.rs index 9ed259c7c..ef76c986f 100644 --- a/src/analysis/enums.rs +++ b/src/analysis/enums.rs @@ -1,5 +1,5 @@ use super::{function_parameters::TransformationType, imports::Imports, info_base::InfoBase, *}; -use crate::{config::gobjects::GObject, env::Env, library, nameutil::*, traits::*}; +use crate::{config::gobjects::GObject, env::Env, nameutil::*, traits::*}; use log::info; use std::ops::Deref; @@ -112,7 +112,7 @@ pub fn new(env: &Env, obj: &GObject, imports: &mut Imports) -> Option { } } - let specials = special_functions::extract(&mut functions); + let specials = special_functions::extract(&mut functions, type_); special_functions::analyze_imports(&specials, imports); diff --git a/src/analysis/object.rs b/src/analysis/object.rs index b21e11f20..317bae192 100644 --- a/src/analysis/object.rs +++ b/src/analysis/object.rs @@ -88,7 +88,7 @@ pub fn class(env: &Env, obj: &GObject, deps: &[library::TypeId]) -> Option Some(&mut signatures), Some(deps), ); - let mut specials = special_functions::extract(&mut functions); + let mut specials = special_functions::extract(&mut functions, type_); // `copy` will duplicate an object while `clone` just adds a reference special_functions::unhide(&mut functions, &specials, special_functions::Type::Copy); // these are all automatically derived on objects and compare by pointer. If such functions @@ -97,7 +97,6 @@ pub fn class(env: &Env, obj: &GObject, deps: &[library::TypeId]) -> Option special_functions::Type::Hash, special_functions::Type::Equal, special_functions::Type::Compare, - special_functions::Type::Display, ] { special_functions::unhide(&mut functions, &specials, *t); specials.remove(t); diff --git a/src/analysis/record.rs b/src/analysis/record.rs index d76558578..7b9264d66 100644 --- a/src/analysis/record.rs +++ b/src/analysis/record.rs @@ -92,7 +92,7 @@ pub fn new(env: &Env, obj: &GObject) -> Option { None, None, ); - let specials = special_functions::extract(&mut functions); + let specials = special_functions::extract(&mut functions, type_); let (version, deprecated_version) = info_base::versions( env, diff --git a/src/analysis/special_functions.rs b/src/analysis/special_functions.rs index 28eba95de..2ef285fc1 100644 --- a/src/analysis/special_functions.rs +++ b/src/analysis/special_functions.rs @@ -3,7 +3,7 @@ use crate::{ functions::{Info as FuncInfo, Visibility}, imports::Imports, }, - library::TypeId, + library::{Type as LibType, TypeId}, }; use std::{collections::BTreeMap, str::FromStr}; @@ -22,7 +22,7 @@ pub enum Type { impl FromStr for Type { type Err = String; - fn from_str(s: &str) -> Result { + fn from_str(s: &str) -> Result { use self::Type::*; match s { "compare" => Ok(Compare), @@ -39,9 +39,24 @@ impl FromStr for Type { } } -pub type Infos = BTreeMap; // Type => glib_name +impl Type { + fn extract(s: &str) -> Option { + s.parse().ok().or_else(|| match s { + "get_name" | "name" => Some(Self::Display), + _ => None, + }) + } +} + +#[derive(Debug, Clone)] +pub struct Info { + pub glib_name: String, + pub returns_static_ref: bool, +} -fn update_func(func: &mut FuncInfo, type_: Type) -> bool { +pub type Infos = BTreeMap; + +fn update_func(func: &mut FuncInfo, type_: Type, parent_type: &LibType) -> bool { if func.visibility != Visibility::Comment { func.visibility = visibility(type_); } @@ -62,31 +77,46 @@ fn update_func(func: &mut FuncInfo, type_: Type) -> bool { return false; } - if let Some(par) = func.ret.parameter.as_mut() { - // I assume `to_string` functions never return `NULL` - *par.nullable = false; + if func.name == "to_string" { + // Rename to to_str to make sure it doesn't clash with ToString::to_string + func.name = "to_str".to_owned(); + + // As to not change old code behaviour, assume non-nullability outside + // enums and flags only. Function inside enums and flags have been + // appropriately marked in Gir. + if !matches!(parent_type, LibType::Enumeration(_) | LibType::Bitfield(_)) { + if let Some(par) = func.ret.parameter.as_mut() { + *par.nullable = false; + } + } } - if func.visibility != Visibility::Private { + // Cannot generate Display implementation for Option<> + if func + .ret + .parameter + .as_ref() + .map_or(false, |ret| *ret.nullable) + { return false; } } true } -pub fn extract(functions: &mut Vec) -> Infos { - let mut specials = BTreeMap::new(); +pub fn extract(functions: &mut Vec, parent_type: &LibType) -> Infos { + let mut specials = Infos::new(); let mut has_copy = false; let mut has_free = false; let mut destroy = None; for (pos, func) in functions.iter_mut().enumerate() { - if let Ok(type_) = Type::from_str(&func.name) { + if let Some(type_) = Type::extract(&func.name) { if func.name == "destroy" { destroy = Some((func.glib_name.clone(), pos)); continue; } - if !update_func(func, type_) { + if !update_func(func, type_, parent_type) { continue; } if func.name == "copy" { @@ -94,15 +124,42 @@ pub fn extract(functions: &mut Vec) -> Infos { } else if func.name == "free" { has_free = true; } - specials.insert(type_, func.glib_name.clone()); + + let return_transfer_none = func.ret.parameter.as_ref().map_or(false, |ret| { + ret.transfer == crate::library::Transfer::None + // This is enforced already, otherwise no impl Display can be generated. + && !*ret.nullable + }); + + // Assume only enumerations and bitfields can return static strings + let returns_static_ref = type_ == Type::Display + && return_transfer_none + && matches!(parent_type, LibType::Enumeration(_) | LibType::Bitfield(_)) + // We cannot mandate returned lifetime if this is not generated. + // (And this prevents an unused std::ffi::CStr from being emitted below) + && func.status.need_generate(); + + specials.insert( + type_, + Info { + glib_name: func.glib_name.clone(), + returns_static_ref, + }, + ); } } if has_copy && !has_free { if let Some((glib_name, pos)) = destroy { let ty_ = Type::from_str("destroy").unwrap(); - update_func(&mut functions[pos], ty_); - specials.insert(ty_, glib_name); + update_func(&mut functions[pos], ty_, parent_type); + specials.insert( + ty_, + Info { + glib_name, + returns_static_ref: false, + }, + ); } } @@ -123,7 +180,7 @@ pub fn unhide(functions: &mut Vec, specials: &Infos, type_: Type) { if let Some(func) = specials.get(&type_) { let func = functions .iter_mut() - .find(|f| f.glib_name == *func && f.visibility != Visibility::Comment); + .find(|f| f.glib_name == func.glib_name && f.visibility != Visibility::Comment); if let Some(func) = func { func.visibility = Visibility::Public; } @@ -132,10 +189,15 @@ pub fn unhide(functions: &mut Vec, specials: &Infos, type_: Type) { pub fn analyze_imports(specials: &Infos, imports: &mut Imports) { use self::Type::*; - for type_ in specials.keys() { + for (type_, info) in specials { match *type_ { Compare => imports.add("std::cmp"), - Display => imports.add("std::fmt"), + Display => { + imports.add("std::fmt"); + if info.returns_static_ref { + imports.add("std::ffi::CStr"); + } + } Hash => imports.add("std::hash"), _ => {} } diff --git a/src/codegen/enums.rs b/src/codegen/enums.rs index f429fccb0..2f911920c 100644 --- a/src/codegen/enums.rs +++ b/src/codegen/enums.rs @@ -119,11 +119,20 @@ fn generate_enum( let functions = analysis.functions(); if !functions.is_empty() { + let static_tostring = analysis + .specials + .get(&crate::analysis::special_functions::Type::Display) + .and_then(|f| if f.returns_static_ref { Some(f) } else { None }); + writeln!(w)?; version_condition(w, env, enum_.version, false, 0)?; write!(w, "impl {} {{", analysis.name)?; for func_analysis in functions { - function::generate(w, env, func_analysis, false, false, 1)?; + if Some(&func_analysis.glib_name) == static_tostring.map(|t| &t.glib_name) { + function::generate_static_to_str(w, env, func_analysis)?; + } else { + function::generate(w, env, func_analysis, false, false, 1)?; + } } writeln!(w, "}}")?; } diff --git a/src/codegen/function.rs b/src/codegen/function.rs index 8c7ffb1ae..8a29c3a3c 100644 --- a/src/codegen/function.rs +++ b/src/codegen/function.rs @@ -456,3 +456,39 @@ pub fn body_chunk_futures( Ok(body) } + +pub(super) fn generate_static_to_str( + w: &mut dyn Write, + env: &Env, + function: &analysis::functions::Info, +) -> Result<()> { + writeln!(w)?; + version_condition(w, env, function.version, false, 1)?; + + let visibility = match function.visibility { + Visibility::Public => "pub ", + _ => "", + }; + + writeln!( + w, + "\ +\t{visibility}fn {rust_fn_name}<'a>(self) -> &'a str {{ +\t\tunsafe {{ +\t\t\tCStr::from_ptr( +\t\t\t\t{ns}::{glib_fn_name}(self.to_glib()) +\t\t\t\t\t.as_ref() +\t\t\t\t\t.expect(\"{glib_fn_name} returned NULL\"), +\t\t\t) +\t\t\t.to_str() +\t\t\t.expect(\"{glib_fn_name} returned an invalid string\") +\t\t}} +\t}}", + visibility = visibility, + rust_fn_name = function.name, + ns = env.main_sys_crate_name(), + glib_fn_name = function.glib_name, + )?; + + Ok(()) +} diff --git a/src/codegen/record.rs b/src/codegen/record.rs index 201c72355..a310f8aaf 100644 --- a/src/codegen/record.rs +++ b/src/codegen/record.rs @@ -39,8 +39,8 @@ pub fn generate(w: &mut dyn Write, env: &Env, analysis: &analysis::record::Info) env, &analysis.name, &type_.c_type, - ref_fn, - unref_fn, + &ref_fn.glib_name, + &unref_fn.glib_name, analysis.glib_get_type.as_ref().map(|(f, v)| { if v > &analysis.version { (f.clone(), *v) @@ -59,8 +59,8 @@ pub fn generate(w: &mut dyn Write, env: &Env, analysis: &analysis::record::Info) env, &analysis.name, &type_.c_type, - copy_fn, - free_fn, + ©_fn.glib_name, + &free_fn.glib_name, &analysis.init_function_expression, &analysis.clear_function_expression, analysis.glib_get_type.as_ref().map(|(f, v)| { diff --git a/src/codegen/trait_impls.rs b/src/codegen/trait_impls.rs index 2bd51a819..d2735c29e 100644 --- a/src/codegen/trait_impls.rs +++ b/src/codegen/trait_impls.rs @@ -16,8 +16,8 @@ pub fn generate( specials: &Infos, trait_name: Option<&str>, ) -> Result<()> { - for (type_, name) in specials.iter() { - if let Some(info) = lookup(functions, name) { + for (type_, special_info) in specials.iter() { + if let Some(info) = lookup(functions, &special_info.glib_name) { match *type_ { Type::Compare => { if !specials.contains_key(&Type::Equal) { @@ -40,7 +40,7 @@ pub fn generate( fn lookup<'a>(functions: &'a [Info], name: &str) -> Option<&'a Info> { functions .iter() - .find(|f| f.status.need_generate() && f.glib_name == name) + .find(|f| !f.status.ignored() && f.glib_name == name) } fn generate_call(func_name: &str, args: &[&str], trait_name: Option<&str>) -> String { From 37366a2950dbdb9ea9ccb1e298436dae612a0220 Mon Sep 17 00:00:00 2001 From: Marijn Suijten Date: Sat, 21 Nov 2020 22:07:46 +0100 Subject: [PATCH 17/26] enums: Do not inherit InfoBase to get rid of unused imports --- src/analysis/enums.rs | 35 +++++++---------------------------- src/codegen/enums.rs | 6 +++++- 2 files changed, 12 insertions(+), 29 deletions(-) diff --git a/src/analysis/enums.rs b/src/analysis/enums.rs index ef76c986f..9a1dc3dde 100644 --- a/src/analysis/enums.rs +++ b/src/analysis/enums.rs @@ -1,20 +1,15 @@ -use super::{function_parameters::TransformationType, imports::Imports, info_base::InfoBase, *}; +use super::{function_parameters::TransformationType, imports::Imports, *}; use crate::{config::gobjects::GObject, env::Env, nameutil::*, traits::*}; use log::info; -use std::ops::Deref; #[derive(Debug, Default)] pub struct Info { - pub base: InfoBase, -} - -impl Deref for Info { - type Target = InfoBase; - - fn deref(&self) -> &InfoBase { - &self.base - } + pub full_name: String, + pub type_id: library::TypeId, + pub name: String, + pub functions: Vec, + pub specials: special_functions::Infos, } impl Info { @@ -116,29 +111,13 @@ pub fn new(env: &Env, obj: &GObject, imports: &mut Imports) -> Option { special_functions::analyze_imports(&specials, imports); - let (version, deprecated_version) = info_base::versions( - env, - obj, - &functions, - enumeration.version, - enumeration.deprecated_version, - ); - - let base = InfoBase { + let info = Info { full_name: obj.name.clone(), type_id: enumeration_tid, name: name.to_owned(), functions, specials, - // TODO: Don't use! - imports: Imports::new(&env.library), - version, - deprecated_version, - cfg_condition: obj.cfg_condition.clone(), - concurrency: obj.concurrency, }; - let info = Info { base }; - Some(info) } diff --git a/src/codegen/enums.rs b/src/codegen/enums.rs index 2f911920c..f3920b4b1 100644 --- a/src/codegen/enums.rs +++ b/src/codegen/enums.rs @@ -116,7 +116,11 @@ fn generate_enum( }}" )?; - let functions = analysis.functions(); + let functions = analysis + .functions + .iter() + .filter(|f| f.status.need_generate()) + .collect::>(); if !functions.is_empty() { let static_tostring = analysis From 6a43595097297e09fdc9a680f7edbaf579c75065 Mon Sep 17 00:00:00 2001 From: Marijn Suijten Date: Sat, 21 Nov 2020 22:16:54 +0100 Subject: [PATCH 18/26] analysis: Move enum discovery out of generic analyze() function --- src/analysis/mod.rs | 40 +++++++++++++++++++++++++++------------- 1 file changed, 27 insertions(+), 13 deletions(-) diff --git a/src/analysis/mod.rs b/src/analysis/mod.rs index 31cf7b119..3e25db165 100644 --- a/src/analysis/mod.rs +++ b/src/analysis/mod.rs @@ -64,9 +64,6 @@ pub fn run(env: &mut Env) { to_analyze.push((tid, deps)); } - let mut enum_imports = Imports::new(&env.library); - enum_imports.add("glib::translate::*"); - let mut analyzed = 1; while analyzed > 0 { analyzed = 0; @@ -76,15 +73,13 @@ pub fn run(env: &mut Env) { new_to_analyze.push((tid, deps.clone())); continue; } - analyze(env, tid, deps, &mut enum_imports); + analyze(env, tid, deps); analyzed += 1; } to_analyze = new_to_analyze; } - env.analysis.enum_imports = enum_imports; - if !to_analyze.is_empty() { error!( "Not analyzed {} objects due unfinished dependencies", @@ -93,12 +88,37 @@ pub fn run(env: &mut Env) { return; } + analyze_enums(env); + analyze_constants(env); // Analyze free functions as the last step once all types are analyzed analyze_global_functions(env); } +fn analyze_enums(env: &mut Env) { + let mut imports = Imports::new(&env.library); + imports.add("glib::translate::*"); + + for obj in env.config.objects.values() { + if obj.status.ignored() { + continue; + } + let tid = match env.library.find_type(0, &obj.name) { + Some(x) => x, + None => continue, + }; + + if let Type::Enumeration(_) = env.library.type_(tid) { + if let Some(info) = enums::new(env, obj, &mut imports) { + env.analysis.enumerations.push(info); + } + } + } + + env.analysis.enum_imports = imports; +} + fn analyze_global_functions(env: &mut Env) { let ns = env.library.namespace(library::MAIN_NAMESPACE); @@ -161,7 +181,7 @@ fn analyze_constants(env: &mut Env) { env.analysis.constants = constants::analyze(env, &constants, obj); } -fn analyze(env: &mut Env, tid: TypeId, deps: &[TypeId], enum_imports: &mut Imports) { +fn analyze(env: &mut Env, tid: TypeId, deps: &[TypeId]) { let full_name = tid.full_name(&env.library); let obj = match env.config.objects.get(&*full_name) { Some(obj) => obj, @@ -183,12 +203,6 @@ fn analyze(env: &mut Env, tid: TypeId, deps: &[TypeId], enum_imports: &mut Impor env.analysis.records.insert(full_name, info); } } - Type::Enumeration(_) => { - // Cannot mutably borrow env.analysis.enum_imports here - if let Some(info) = enums::new(env, obj, enum_imports) { - env.analysis.enumerations.push(info); - } - } _ => {} } } From e549975b156139e67b8325e9e74e76ef11ff8f9f Mon Sep 17 00:00:00 2001 From: Marijn Suijten Date: Sat, 21 Nov 2020 22:47:04 +0100 Subject: [PATCH 19/26] Copypaste enum analysis and function generation to bitfield --- src/analysis/flags.rs | 117 ++++++++++++++++++++++++++++++++++++++++++ src/analysis/mod.rs | 30 +++++++++++ src/codegen/flags.rs | 101 ++++++++++++++++++++---------------- 3 files changed, 204 insertions(+), 44 deletions(-) create mode 100644 src/analysis/flags.rs diff --git a/src/analysis/flags.rs b/src/analysis/flags.rs new file mode 100644 index 000000000..8b8dff119 --- /dev/null +++ b/src/analysis/flags.rs @@ -0,0 +1,117 @@ +use super::{function_parameters::TransformationType, imports::Imports, *}; +use crate::{config::gobjects::GObject, env::Env, nameutil::*, traits::*}; + +use log::info; + +#[derive(Debug, Default)] +pub struct Info { + pub full_name: String, + pub type_id: library::TypeId, + pub name: String, + pub functions: Vec, + pub specials: special_functions::Infos, +} + +impl Info { + pub fn type_<'a>(&self, library: &'a library::Library) -> &'a library::Bitfield { + let type_ = library + .type_(self.type_id) + .maybe_ref() + .unwrap_or_else(|| panic!("{} is not an flags.", self.full_name)); + type_ + } +} + +pub fn new(env: &Env, obj: &GObject, imports: &mut Imports) -> Option { + info!("Analyzing flags {}", obj.name); + + if !obj.status.need_generate() { + return None; + } + + if !obj + .type_id + .map_or(false, |tid| tid.ns_id == namespaces::MAIN) + { + return None; + } + + let flags_tid = env.library.find_type(0, &obj.name)?; + let type_ = env.type_(flags_tid); + let flags: &library::Bitfield = type_.maybe_ref()?; + + let name = split_namespace_name(&obj.name).1; + + // Mark the type as available within the bitfield namespace: + imports.add_defined(&format!("crate::{}", name)); + + let has_get_type = flags.glib_get_type.is_some(); + if has_get_type { + imports.add("glib::Type"); + imports.add("glib::StaticType"); + imports.add("glib::value::SetValue"); + imports.add("glib::value::FromValue"); + imports.add("glib::value::FromValueOptional"); + } + + if obj.generate_display_trait { + imports.add("std::fmt"); + } + + let mut functions = functions::analyze( + env, + &flags.functions, + flags_tid, + false, + false, + obj, + imports, + None, + None, + ); + + // Gir does not currently mark the first parameter of associated bitfield functions - + // that are identical to its bitfield type - as instance parameter since most languages + // do not support this. + for f in &mut functions { + if f.parameters.c_parameters.is_empty() { + continue; + } + + let first_param = &mut f.parameters.c_parameters[0]; + + if first_param.typ == flags_tid { + first_param.instance_parameter = true; + + let t = f + .parameters + .transformations + .iter_mut() + .find(|t| t.ind_c == 0) + .unwrap(); + + if let TransformationType::ToGlibScalar { name, .. } = &mut t.transformation_type { + *name = "self".to_owned(); + } else { + panic!( + "Bitfield function instance param must be passed as scalar, not {:?}", + t.transformation_type + ); + } + } + } + + let specials = special_functions::extract(&mut functions, type_); + + special_functions::analyze_imports(&specials, imports); + + let info = Info { + full_name: obj.name.clone(), + type_id: flags_tid, + name: name.to_owned(), + functions, + specials, + }; + + Some(info) +} diff --git a/src/analysis/mod.rs b/src/analysis/mod.rs index 3e25db165..c540fd4b6 100644 --- a/src/analysis/mod.rs +++ b/src/analysis/mod.rs @@ -15,6 +15,7 @@ pub mod constants; pub mod conversion_type; pub mod enums; pub mod ffi_type; +pub mod flags; pub mod function_parameters; pub mod functions; pub mod general; @@ -48,6 +49,9 @@ pub struct Analysis { pub constants: Vec, pub enumerations: Vec, pub enum_imports: Imports, + + pub flags: Vec, + pub flags_imports: Imports, } pub fn run(env: &mut Env) { @@ -90,6 +94,8 @@ pub fn run(env: &mut Env) { analyze_enums(env); + analyze_flags(env); + analyze_constants(env); // Analyze free functions as the last step once all types are analyzed @@ -119,6 +125,30 @@ fn analyze_enums(env: &mut Env) { env.analysis.enum_imports = imports; } +fn analyze_flags(env: &mut Env) { + let mut imports = Imports::new(&env.library); + imports.add("glib::translate::*"); + imports.add("bitflags::bitflags"); + + for obj in env.config.objects.values() { + if obj.status.ignored() { + continue; + } + let tid = match env.library.find_type(0, &obj.name) { + Some(x) => x, + None => continue, + }; + + if let Type::Bitfield(_) = env.library.type_(tid) { + if let Some(info) = flags::new(env, obj, &mut imports) { + env.analysis.flags.push(info); + } + } + } + + env.analysis.flags_imports = imports; +} + fn analyze_global_functions(env: &mut Env) { let ns = env.library.namespace(library::MAIN_NAMESPACE); diff --git a/src/codegen/flags.rs b/src/codegen/flags.rs index a6240d5e6..b2f4df283 100644 --- a/src/codegen/flags.rs +++ b/src/codegen/flags.rs @@ -1,5 +1,6 @@ +use super::{function, trait_impls}; use crate::{ - analysis::{imports::Imports, namespaces}, + analysis::flags::Info, codegen::general::{ self, cfg_deprecated, derives, version_condition, version_condition_string, }, @@ -16,54 +17,26 @@ use std::{ }; pub fn generate(env: &Env, root_path: &Path, mod_rs: &mut Vec) { - let configs: Vec<&GObject> = env - .config - .objects - .values() - .filter(|c| { - c.status.need_generate() && c.type_id.map_or(false, |tid| tid.ns_id == namespaces::MAIN) - }) - .collect(); - let has_any = configs - .iter() - .any(|c| matches!(*env.library.type_(c.type_id.unwrap()), Type::Bitfield(_))); - - if !has_any { + if env.analysis.flags.is_empty() { return; } let path = root_path.join("flags.rs"); file_saver::save_to_file(path, env.config.make_backup, |w| { - let mut imports = Imports::new(&env.library); - imports.add("glib::translate::*"); - imports.add("bitflags::bitflags"); - - for config in &configs { - if let Type::Bitfield(ref flags) = *env.library.type_(config.type_id.unwrap()) { - if flags.glib_get_type.is_some() { - imports.add("glib::Type"); - imports.add("glib::StaticType"); - imports.add("glib::value::SetValue"); - imports.add("glib::value::FromValue"); - imports.add("glib::value::FromValueOptional"); - break; - } - } - } - general::start_comments(w, &env.config)?; - general::uses(w, env, &imports)?; + general::uses(w, env, &env.analysis.flags_imports)?; writeln!(w)?; mod_rs.push("\nmod flags;".into()); - for config in &configs { - if let Type::Bitfield(ref flags) = *env.library.type_(config.type_id.unwrap()) { - if let Some(cfg) = version_condition_string(env, flags.version, false, 0) { - mod_rs.push(cfg); - } - mod_rs.push(format!("pub use self::flags::{};", flags.name)); - generate_flags(env, w, flags, config)?; + for flags_analysis in &env.analysis.flags { + let config = &env.config.objects[&flags_analysis.full_name]; + let flags = flags_analysis.type_(&env.library); + + if let Some(cfg) = version_condition_string(env, flags.version, false, 0) { + mod_rs.push(cfg); } + mod_rs.push(format!("pub use self::flags::{};", flags.name)); + generate_flags(env, w, flags, config, flags_analysis)?; } Ok(()) @@ -71,7 +44,13 @@ pub fn generate(env: &Env, root_path: &Path, mod_rs: &mut Vec) { } #[allow(clippy::write_literal)] -fn generate_flags(env: &Env, w: &mut dyn Write, flags: &Bitfield, config: &GObject) -> Result<()> { +fn generate_flags( + env: &Env, + w: &mut dyn Write, + flags: &Bitfield, + config: &GObject, + analysis: &Info, +) -> Result<()> { let sys_crate_name = env.main_sys_crate_name(); cfg_deprecated(w, env, flags.deprecated_version, false, 0)?; version_condition(w, env, flags.version, false, 0)?; @@ -106,12 +85,46 @@ fn generate_flags(env: &Env, w: &mut dyn Write, flags: &Bitfield, config: &GObje writeln!( w, - "{}", - " } -} -" + " }} +}}" )?; + let functions = analysis + .functions + .iter() + .filter(|f| f.status.need_generate()) + .collect::>(); + + if !functions.is_empty() { + let static_tostring = analysis + .specials + .get(&crate::analysis::special_functions::Type::Display) + .and_then(|f| if f.returns_static_ref { Some(f) } else { None }); + + writeln!(w)?; + version_condition(w, env, flags.version, false, 0)?; + write!(w, "impl {} {{", analysis.name)?; + for func_analysis in functions { + if Some(&func_analysis.glib_name) == static_tostring.map(|t| &t.glib_name) { + function::generate_static_to_str(w, env, func_analysis)?; + } else { + function::generate(w, env, func_analysis, false, false, 1)?; + } + } + writeln!(w, "}}")?; + } + + trait_impls::generate( + w, + env, + &analysis.name, + &analysis.functions, + &analysis.specials, + None, + )?; + + writeln!(w)?; + version_condition(w, env, flags.version, false, 0)?; writeln!( w, From 518d2772b262704cd6f0307d3879ad59ea377a55 Mon Sep 17 00:00:00 2001 From: Marijn Suijten Date: Mon, 30 Nov 2020 21:15:16 +0100 Subject: [PATCH 20/26] codegen/flags: Add missing display trait generation Analysis was copied from enums which adds an std::fmt when generate_display_trait is true, but the codegen for it was missing. Instead of removing the import, provide a simple Display trait that forwards to bitflags' Debug implementation. --- src/codegen/flags.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/codegen/flags.rs b/src/codegen/flags.rs index b2f4df283..264cfab75 100644 --- a/src/codegen/flags.rs +++ b/src/codegen/flags.rs @@ -125,6 +125,20 @@ fn generate_flags( writeln!(w)?; + if config.generate_display_trait { + // Generate Display trait implementation. + version_condition(w, env, flags.version, false, 0)?; + writeln!( + w, + "impl fmt::Display for {0} {{\n\ + \tfn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {{\n\ + \t\t::fmt(self, f)\n\ + \t}}\n\ + }}\n", + flags.name + )?; + } + version_condition(w, env, flags.version, false, 0)?; writeln!( w, From 68760c0b70f4034654853b6a1adccb131cdf824e Mon Sep 17 00:00:00 2001 From: Marijn Suijten Date: Mon, 30 Nov 2020 21:30:44 +0100 Subject: [PATCH 21/26] codegen: Don't emit Rust Display impl when C function is available When get_name or to_string is available and adheres to the right conditions a Display impl leveraging this C implementation is generated. This will clash with a pure Rust impl if generate_display_trait is true which should be disabled to prefer the C function call instead. --- src/codegen/enums.rs | 14 +++++++++----- src/codegen/flags.rs | 14 +++++++++----- src/codegen/object.rs | 3 ++- 3 files changed, 20 insertions(+), 11 deletions(-) diff --git a/src/codegen/enums.rs b/src/codegen/enums.rs index f3920b4b1..2a27ab956 100644 --- a/src/codegen/enums.rs +++ b/src/codegen/enums.rs @@ -1,6 +1,7 @@ use super::{function, trait_impls}; use crate::{ analysis::enums::Info, + analysis::special_functions::Type, codegen::general::{ self, cfg_deprecated, derives, version_condition, version_condition_no_doc, version_condition_string, @@ -123,10 +124,13 @@ fn generate_enum( .collect::>(); if !functions.is_empty() { - let static_tostring = analysis - .specials - .get(&crate::analysis::special_functions::Type::Display) - .and_then(|f| if f.returns_static_ref { Some(f) } else { None }); + let static_tostring = analysis.specials.get(&Type::Display).and_then(|f| { + if f.returns_static_ref { + Some(f) + } else { + None + } + }); writeln!(w)?; version_condition(w, env, enum_.version, false, 0)?; @@ -152,7 +156,7 @@ fn generate_enum( writeln!(w)?; - if config.generate_display_trait { + if config.generate_display_trait && !analysis.specials.contains_key(&Type::Display) { // Generate Display trait implementation. version_condition(w, env, enum_.version, false, 0)?; writeln!( diff --git a/src/codegen/flags.rs b/src/codegen/flags.rs index 264cfab75..c5944728d 100644 --- a/src/codegen/flags.rs +++ b/src/codegen/flags.rs @@ -1,6 +1,7 @@ use super::{function, trait_impls}; use crate::{ analysis::flags::Info, + analysis::special_functions::Type, codegen::general::{ self, cfg_deprecated, derives, version_condition, version_condition_string, }, @@ -96,10 +97,13 @@ fn generate_flags( .collect::>(); if !functions.is_empty() { - let static_tostring = analysis - .specials - .get(&crate::analysis::special_functions::Type::Display) - .and_then(|f| if f.returns_static_ref { Some(f) } else { None }); + let static_tostring = analysis.specials.get(&Type::Display).and_then(|f| { + if f.returns_static_ref { + Some(f) + } else { + None + } + }); writeln!(w)?; version_condition(w, env, flags.version, false, 0)?; @@ -125,7 +129,7 @@ fn generate_flags( writeln!(w)?; - if config.generate_display_trait { + if config.generate_display_trait && !analysis.specials.contains_key(&Type::Display) { // Generate Display trait implementation. version_condition(w, env, flags.version, false, 0)?; writeln!( diff --git a/src/codegen/object.rs b/src/codegen/object.rs index 9088e05a8..a3dc98ab3 100644 --- a/src/codegen/object.rs +++ b/src/codegen/object.rs @@ -1,5 +1,6 @@ use super::{child_properties, function, general, properties, signal, trait_impls}; use crate::{ + analysis::special_functions::Type, analysis::{ self, rust_type::{rust_type, rust_type_full}, @@ -133,7 +134,7 @@ pub fn generate( generate_trait(w, env, analysis)?; } - if generate_display_trait { + if generate_display_trait && !analysis.specials.contains_key(&Type::Display) { writeln!(w, "\nimpl fmt::Display for {} {{", analysis.name,)?; // Generate Display trait implementation. writeln!( From a1cfe6515cc82b566f23772fa86d8b81d8c217c6 Mon Sep 17 00:00:00 2001 From: Marijn Suijten Date: Wed, 2 Dec 2020 23:01:38 +0100 Subject: [PATCH 22/26] special_functions: Add version condition to imports as well Complements: 5ce31f2 ("codegen: Add version condition on special function traits") --- src/analysis/special_functions.rs | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/analysis/special_functions.rs b/src/analysis/special_functions.rs index 2ef285fc1..19502f923 100644 --- a/src/analysis/special_functions.rs +++ b/src/analysis/special_functions.rs @@ -4,6 +4,7 @@ use crate::{ imports::Imports, }, library::{Type as LibType, TypeId}, + version::Version, }; use std::{collections::BTreeMap, str::FromStr}; @@ -52,6 +53,7 @@ impl Type { pub struct Info { pub glib_name: String, pub returns_static_ref: bool, + pub version: Option, } pub type Infos = BTreeMap; @@ -144,6 +146,7 @@ pub fn extract(functions: &mut Vec, parent_type: &LibType) -> Infos { Info { glib_name: func.glib_name.clone(), returns_static_ref, + version: func.version, }, ); } @@ -152,12 +155,14 @@ pub fn extract(functions: &mut Vec, parent_type: &LibType) -> Infos { if has_copy && !has_free { if let Some((glib_name, pos)) = destroy { let ty_ = Type::from_str("destroy").unwrap(); - update_func(&mut functions[pos], ty_, parent_type); + let func = &mut functions[pos]; + update_func(func, ty_, parent_type); specials.insert( ty_, Info { glib_name, returns_static_ref: false, + version: func.version, }, ); } @@ -191,14 +196,14 @@ pub fn analyze_imports(specials: &Infos, imports: &mut Imports) { use self::Type::*; for (type_, info) in specials { match *type_ { - Compare => imports.add("std::cmp"), + Compare => imports.add_with_version("std::cmp", info.version), Display => { - imports.add("std::fmt"); + imports.add_with_version("std::fmt", info.version); if info.returns_static_ref { - imports.add("std::ffi::CStr"); + imports.add_with_version("std::ffi::CStr", info.version); } } - Hash => imports.add("std::hash"), + Hash => imports.add_with_version("std::hash", info.version), _ => {} } } From bf7dd099e29774687b0636491b9bae0c3d58683b Mon Sep 17 00:00:00 2001 From: Marijn Suijten Date: Sat, 5 Dec 2020 23:32:33 +0100 Subject: [PATCH 23/26] special_functions: Only override to_string nullability if not trusted --- src/analysis/enums.rs | 2 +- src/analysis/flags.rs | 2 +- src/analysis/object.rs | 2 +- src/analysis/record.rs | 2 +- src/analysis/special_functions.rs | 13 ++++++++----- 5 files changed, 12 insertions(+), 9 deletions(-) diff --git a/src/analysis/enums.rs b/src/analysis/enums.rs index 9a1dc3dde..ace41d290 100644 --- a/src/analysis/enums.rs +++ b/src/analysis/enums.rs @@ -107,7 +107,7 @@ pub fn new(env: &Env, obj: &GObject, imports: &mut Imports) -> Option { } } - let specials = special_functions::extract(&mut functions, type_); + let specials = special_functions::extract(&mut functions, type_, obj); special_functions::analyze_imports(&specials, imports); diff --git a/src/analysis/flags.rs b/src/analysis/flags.rs index 8b8dff119..e04f73698 100644 --- a/src/analysis/flags.rs +++ b/src/analysis/flags.rs @@ -101,7 +101,7 @@ pub fn new(env: &Env, obj: &GObject, imports: &mut Imports) -> Option { } } - let specials = special_functions::extract(&mut functions, type_); + let specials = special_functions::extract(&mut functions, type_, obj); special_functions::analyze_imports(&specials, imports); diff --git a/src/analysis/object.rs b/src/analysis/object.rs index 317bae192..dc66d22e8 100644 --- a/src/analysis/object.rs +++ b/src/analysis/object.rs @@ -88,7 +88,7 @@ pub fn class(env: &Env, obj: &GObject, deps: &[library::TypeId]) -> Option Some(&mut signatures), Some(deps), ); - let mut specials = special_functions::extract(&mut functions, type_); + let mut specials = special_functions::extract(&mut functions, type_, obj); // `copy` will duplicate an object while `clone` just adds a reference special_functions::unhide(&mut functions, &specials, special_functions::Type::Copy); // these are all automatically derived on objects and compare by pointer. If such functions diff --git a/src/analysis/record.rs b/src/analysis/record.rs index 7b9264d66..9adc46504 100644 --- a/src/analysis/record.rs +++ b/src/analysis/record.rs @@ -92,7 +92,7 @@ pub fn new(env: &Env, obj: &GObject) -> Option { None, None, ); - let specials = special_functions::extract(&mut functions, type_); + let specials = special_functions::extract(&mut functions, type_, obj); let (version, deprecated_version) = info_base::versions( env, diff --git a/src/analysis/special_functions.rs b/src/analysis/special_functions.rs index 19502f923..55bb73c4c 100644 --- a/src/analysis/special_functions.rs +++ b/src/analysis/special_functions.rs @@ -3,6 +3,7 @@ use crate::{ functions::{Info as FuncInfo, Visibility}, imports::Imports, }, + config::GObject, library::{Type as LibType, TypeId}, version::Version, }; @@ -58,7 +59,7 @@ pub struct Info { pub type Infos = BTreeMap; -fn update_func(func: &mut FuncInfo, type_: Type, parent_type: &LibType) -> bool { +fn update_func(func: &mut FuncInfo, type_: Type, parent_type: &LibType, obj: &GObject) -> bool { if func.visibility != Visibility::Comment { func.visibility = visibility(type_); } @@ -86,7 +87,9 @@ fn update_func(func: &mut FuncInfo, type_: Type, parent_type: &LibType) -> bool // As to not change old code behaviour, assume non-nullability outside // enums and flags only. Function inside enums and flags have been // appropriately marked in Gir. - if !matches!(parent_type, LibType::Enumeration(_) | LibType::Bitfield(_)) { + if !obj.trust_return_value_nullability + && !matches!(parent_type, LibType::Enumeration(_) | LibType::Bitfield(_)) + { if let Some(par) = func.ret.parameter.as_mut() { *par.nullable = false; } @@ -106,7 +109,7 @@ fn update_func(func: &mut FuncInfo, type_: Type, parent_type: &LibType) -> bool true } -pub fn extract(functions: &mut Vec, parent_type: &LibType) -> Infos { +pub fn extract(functions: &mut Vec, parent_type: &LibType, obj: &GObject) -> Infos { let mut specials = Infos::new(); let mut has_copy = false; let mut has_free = false; @@ -118,7 +121,7 @@ pub fn extract(functions: &mut Vec, parent_type: &LibType) -> Infos { destroy = Some((func.glib_name.clone(), pos)); continue; } - if !update_func(func, type_, parent_type) { + if !update_func(func, type_, parent_type, obj) { continue; } if func.name == "copy" { @@ -156,7 +159,7 @@ pub fn extract(functions: &mut Vec, parent_type: &LibType) -> Infos { if let Some((glib_name, pos)) = destroy { let ty_ = Type::from_str("destroy").unwrap(); let func = &mut functions[pos]; - update_func(func, ty_, parent_type); + update_func(func, ty_, parent_type, obj); specials.insert( ty_, Info { From 18c12a519119d9de234b8ee8c0d8f589bad243e8 Mon Sep 17 00:00:00 2001 From: Marijn Suijten Date: Sun, 6 Dec 2020 13:09:59 +0100 Subject: [PATCH 24/26] Separate special functions from traits --- src/analysis/object.rs | 2 +- src/analysis/record.rs | 13 ++- src/analysis/special_functions.rs | 158 ++++++++++++++++++------------ src/codegen/enums.rs | 24 ++--- src/codegen/flags.rs | 24 ++--- src/codegen/function.rs | 44 ++------- src/codegen/functions.rs | 2 +- src/codegen/mod.rs | 1 + src/codegen/object.rs | 52 ++++++++-- src/codegen/record.rs | 18 +++- src/codegen/special_functions.rs | 60 ++++++++++++ src/codegen/trait_impls.rs | 4 +- 12 files changed, 251 insertions(+), 151 deletions(-) create mode 100644 src/codegen/special_functions.rs diff --git a/src/analysis/object.rs b/src/analysis/object.rs index dc66d22e8..7bc6f8cce 100644 --- a/src/analysis/object.rs +++ b/src/analysis/object.rs @@ -99,7 +99,7 @@ pub fn class(env: &Env, obj: &GObject, deps: &[library::TypeId]) -> Option special_functions::Type::Compare, ] { special_functions::unhide(&mut functions, &specials, *t); - specials.remove(t); + specials.traits_mut().remove(t); } special_functions::analyze_imports(&specials, &mut imports); diff --git a/src/analysis/record.rs b/src/analysis/record.rs index 9adc46504..5dd7311e7 100644 --- a/src/analysis/record.rs +++ b/src/analysis/record.rs @@ -102,8 +102,8 @@ pub fn new(env: &Env, obj: &GObject) -> Option { record.deprecated_version, ); - let is_shared = specials.get(&special_functions::Type::Ref).is_some() - && specials.get(&special_functions::Type::Unref).is_some(); + let is_shared = specials.has_trait(special_functions::Type::Ref) + && specials.has_trait(special_functions::Type::Unref); if is_shared { // `copy` will duplicate a struct while `clone` just adds a reference special_functions::unhide(&mut functions, &specials, special_functions::Type::Copy); @@ -127,7 +127,7 @@ pub fn new(env: &Env, obj: &GObject) -> Option { derives }; - for special in specials.keys() { + for special in specials.traits().keys() { match special { special_functions::Type::Compare => { derives = filter_derives(&derives, &["PartialOrd", "Ord", "PartialEq", "Eq"]); @@ -160,10 +160,9 @@ pub fn new(env: &Env, obj: &GObject) -> Option { // Check if we have to make use of the GType and the generic // boxed functions. if obj.use_boxed_functions - || ((specials.get(&special_functions::Type::Ref).is_none() - || specials.get(&special_functions::Type::Unref).is_none()) - && (specials.get(&special_functions::Type::Copy).is_none() - || specials.get(&special_functions::Type::Free).is_none())) + || !is_shared + && (!specials.has_trait(special_functions::Type::Copy) + || !specials.has_trait(special_functions::Type::Free)) { if let Some((_, get_type_version)) = glib_get_type { if get_type_version > version { diff --git a/src/analysis/special_functions.rs b/src/analysis/special_functions.rs index 55bb73c4c..b09665c80 100644 --- a/src/analysis/special_functions.rs +++ b/src/analysis/special_functions.rs @@ -33,7 +33,7 @@ impl FromStr for Type { "free" | "destroy" => Ok(Free), "is_equal" => Ok(Equal), "ref" | "ref_" => Ok(Ref), - "to_string" => Ok(Display), + "to_string" | "to_str" | "name" | "get_name" => Ok(Display), "unref" => Ok(Unref), "hash" => Ok(Hash), _ => Err(format!("Unknown type '{}'", s)), @@ -41,23 +41,50 @@ impl FromStr for Type { } } -impl Type { - fn extract(s: &str) -> Option { - s.parse().ok().or_else(|| match s { - "get_name" | "name" => Some(Self::Display), - _ => None, - }) - } +#[derive(Debug, Clone)] +pub struct TraitInfo { + pub glib_name: String, + pub version: Option, +} + +type TraitInfos = BTreeMap; + +#[derive(Clone, Copy, Eq, Debug, Ord, PartialEq, PartialOrd)] +pub enum FunctionType { + StaticStringify, } #[derive(Debug, Clone)] -pub struct Info { - pub glib_name: String, - pub returns_static_ref: bool, +pub struct FunctionInfo { + pub type_: FunctionType, pub version: Option, } -pub type Infos = BTreeMap; +type FunctionInfos = BTreeMap; + +#[derive(Debug, Default)] +pub struct Infos { + traits: TraitInfos, + functions: FunctionInfos, +} + +impl Infos { + pub fn traits(&self) -> &TraitInfos { + &self.traits + } + + pub fn traits_mut(&mut self) -> &mut TraitInfos { + &mut self.traits + } + + pub fn has_trait(&self, type_: Type) -> bool { + self.traits.contains_key(&type_) + } + + pub fn functions(&self) -> &FunctionInfos { + &self.functions + } +} fn update_func(func: &mut FuncInfo, type_: Type, parent_type: &LibType, obj: &GObject) -> bool { if func.visibility != Visibility::Comment { @@ -71,52 +98,42 @@ fn update_func(func: &mut FuncInfo, type_: Type, parent_type: &LibType, obj: &GO if !func.parameters.c_parameters[0].instance_parameter { return false; } - if !func - .ret - .parameter - .as_ref() - .map_or(false, |p| p.typ == TypeId::tid_utf8()) - { - return false; - } - if func.name == "to_string" { - // Rename to to_str to make sure it doesn't clash with ToString::to_string - func.name = "to_str".to_owned(); - - // As to not change old code behaviour, assume non-nullability outside - // enums and flags only. Function inside enums and flags have been - // appropriately marked in Gir. - if !obj.trust_return_value_nullability - && !matches!(parent_type, LibType::Enumeration(_) | LibType::Bitfield(_)) - { - if let Some(par) = func.ret.parameter.as_mut() { - *par.nullable = false; + if let Some(ret) = func.ret.parameter.as_mut() { + if ret.typ != TypeId::tid_utf8() { + return false; + } + + if func.name == "to_string" { + // Rename to to_str to make sure it doesn't clash with ToString::to_string + func.name = "to_str".to_owned(); + + // As to not change old code behaviour, assume non-nullability outside + // enums and flags only, and exclusively for to_string. Function inside + // enums and flags have been appropriately marked in Gir. + if !obj.trust_return_value_nullability + && !matches!(parent_type, LibType::Enumeration(_) | LibType::Bitfield(_)) + { + *ret.nullable = false; } } - } - // Cannot generate Display implementation for Option<> - if func - .ret - .parameter - .as_ref() - .map_or(false, |ret| *ret.nullable) - { - return false; + // Cannot generate Display implementation for Option<> + return !*ret.nullable; } } + true } pub fn extract(functions: &mut Vec, parent_type: &LibType, obj: &GObject) -> Infos { - let mut specials = Infos::new(); + let mut specials = Infos::default(); let mut has_copy = false; let mut has_free = false; let mut destroy = None; for (pos, func) in functions.iter_mut().enumerate() { - if let Some(type_) = Type::extract(&func.name) { + if let Ok(type_) = func.name.parse() { if func.name == "destroy" { destroy = Some((func.glib_name.clone(), pos)); continue; @@ -130,25 +147,35 @@ pub fn extract(functions: &mut Vec, parent_type: &LibType, obj: &GObje has_free = true; } - let return_transfer_none = func.ret.parameter.as_ref().map_or(false, |ret| { - ret.transfer == crate::library::Transfer::None - // This is enforced already, otherwise no impl Display can be generated. - && !*ret.nullable - }); + let return_transfer_none = func + .ret + .parameter + .as_ref() + .map_or(false, |ret| ret.transfer == crate::library::Transfer::None); // Assume only enumerations and bitfields can return static strings - let returns_static_ref = type_ == Type::Display - && return_transfer_none + let returns_static_ref = return_transfer_none && matches!(parent_type, LibType::Enumeration(_) | LibType::Bitfield(_)) // We cannot mandate returned lifetime if this is not generated. // (And this prevents an unused std::ffi::CStr from being emitted below) && func.status.need_generate(); - specials.insert( + if returns_static_ref { + // Override the function with a &'static (non allocating) -returning string + // if the transfer type is none and it matches the above heuristics. + specials.functions.insert( + func.glib_name.clone(), + FunctionInfo { + type_: FunctionType::StaticStringify, + version: func.version, + }, + ); + } + + specials.traits.insert( type_, - Info { + TraitInfo { glib_name: func.glib_name.clone(), - returns_static_ref, version: func.version, }, ); @@ -160,11 +187,10 @@ pub fn extract(functions: &mut Vec, parent_type: &LibType, obj: &GObje let ty_ = Type::from_str("destroy").unwrap(); let func = &mut functions[pos]; update_func(func, ty_, parent_type, obj); - specials.insert( + specials.traits.insert( ty_, - Info { + TraitInfo { glib_name, - returns_static_ref: false, version: func.version, }, ); @@ -185,7 +211,7 @@ fn visibility(t: Type) -> Visibility { // Some special functions (e.g. `copy` on refcounted types) should be exposed pub fn unhide(functions: &mut Vec, specials: &Infos, type_: Type) { - if let Some(func) = specials.get(&type_) { + if let Some(func) = specials.traits().get(&type_) { let func = functions .iter_mut() .find(|f| f.glib_name == func.glib_name && f.visibility != Visibility::Comment); @@ -196,18 +222,20 @@ pub fn unhide(functions: &mut Vec, specials: &Infos, type_: Type) { } pub fn analyze_imports(specials: &Infos, imports: &mut Imports) { - use self::Type::*; - for (type_, info) in specials { + for (type_, info) in specials.traits() { + use self::Type::*; match *type_ { Compare => imports.add_with_version("std::cmp", info.version), - Display => { - imports.add_with_version("std::fmt", info.version); - if info.returns_static_ref { - imports.add_with_version("std::ffi::CStr", info.version); - } - } + Display => imports.add_with_version("std::fmt", info.version), Hash => imports.add_with_version("std::hash", info.version), _ => {} } } + for info in specials.functions().values() { + match info.type_ { + FunctionType::StaticStringify => { + imports.add_with_version("std::ffi::CStr", info.version) + } + } + } } diff --git a/src/codegen/enums.rs b/src/codegen/enums.rs index 2a27ab956..9a53147c2 100644 --- a/src/codegen/enums.rs +++ b/src/codegen/enums.rs @@ -124,23 +124,19 @@ fn generate_enum( .collect::>(); if !functions.is_empty() { - let static_tostring = analysis.specials.get(&Type::Display).and_then(|f| { - if f.returns_static_ref { - Some(f) - } else { - None - } - }); - writeln!(w)?; version_condition(w, env, enum_.version, false, 0)?; write!(w, "impl {} {{", analysis.name)?; for func_analysis in functions { - if Some(&func_analysis.glib_name) == static_tostring.map(|t| &t.glib_name) { - function::generate_static_to_str(w, env, func_analysis)?; - } else { - function::generate(w, env, func_analysis, false, false, 1)?; - } + function::generate( + w, + env, + func_analysis, + Some(&analysis.specials), + false, + false, + 1, + )?; } writeln!(w, "}}")?; } @@ -156,7 +152,7 @@ fn generate_enum( writeln!(w)?; - if config.generate_display_trait && !analysis.specials.contains_key(&Type::Display) { + if config.generate_display_trait && !analysis.specials.has_trait(Type::Display) { // Generate Display trait implementation. version_condition(w, env, enum_.version, false, 0)?; writeln!( diff --git a/src/codegen/flags.rs b/src/codegen/flags.rs index c5944728d..ad188684c 100644 --- a/src/codegen/flags.rs +++ b/src/codegen/flags.rs @@ -97,23 +97,19 @@ fn generate_flags( .collect::>(); if !functions.is_empty() { - let static_tostring = analysis.specials.get(&Type::Display).and_then(|f| { - if f.returns_static_ref { - Some(f) - } else { - None - } - }); - writeln!(w)?; version_condition(w, env, flags.version, false, 0)?; write!(w, "impl {} {{", analysis.name)?; for func_analysis in functions { - if Some(&func_analysis.glib_name) == static_tostring.map(|t| &t.glib_name) { - function::generate_static_to_str(w, env, func_analysis)?; - } else { - function::generate(w, env, func_analysis, false, false, 1)?; - } + function::generate( + w, + env, + func_analysis, + Some(&analysis.specials), + false, + false, + 1, + )?; } writeln!(w, "}}")?; } @@ -129,7 +125,7 @@ fn generate_flags( writeln!(w)?; - if config.generate_display_trait && !analysis.specials.contains_key(&Type::Display) { + if config.generate_display_trait && !analysis.specials.has_trait(Type::Display) { // Generate Display trait implementation. version_condition(w, env, flags.version, false, 0)?; writeln!( diff --git a/src/codegen/function.rs b/src/codegen/function.rs index 8a29c3a3c..2330a19f6 100644 --- a/src/codegen/function.rs +++ b/src/codegen/function.rs @@ -5,6 +5,7 @@ use super::{ }, parameter::ToParameter, return_value::{out_parameters_as_return, ToReturnValue}, + special_functions, }; use crate::{ analysis::{ @@ -29,6 +30,7 @@ pub fn generate( w: &mut dyn Write, env: &Env, analysis: &analysis::functions::Info, + special_functions: Option<&analysis::special_functions::Infos>, in_trait: bool, only_declaration: bool, indent: usize, @@ -41,6 +43,12 @@ pub fn generate( return Ok(()); } + if let Some(special_functions) = special_functions { + if special_functions::generate(w, env, analysis, special_functions)? { + return Ok(()); + } + } + let mut commented = false; let mut comment_prefix = ""; let mut pub_prefix = if in_trait { "" } else { "pub " }; @@ -456,39 +464,3 @@ pub fn body_chunk_futures( Ok(body) } - -pub(super) fn generate_static_to_str( - w: &mut dyn Write, - env: &Env, - function: &analysis::functions::Info, -) -> Result<()> { - writeln!(w)?; - version_condition(w, env, function.version, false, 1)?; - - let visibility = match function.visibility { - Visibility::Public => "pub ", - _ => "", - }; - - writeln!( - w, - "\ -\t{visibility}fn {rust_fn_name}<'a>(self) -> &'a str {{ -\t\tunsafe {{ -\t\t\tCStr::from_ptr( -\t\t\t\t{ns}::{glib_fn_name}(self.to_glib()) -\t\t\t\t\t.as_ref() -\t\t\t\t\t.expect(\"{glib_fn_name} returned NULL\"), -\t\t\t) -\t\t\t.to_str() -\t\t\t.expect(\"{glib_fn_name} returned an invalid string\") -\t\t}} -\t}}", - visibility = visibility, - rust_fn_name = function.name, - ns = env.main_sys_crate_name(), - glib_fn_name = function.glib_name, - )?; - - Ok(()) -} diff --git a/src/codegen/functions.rs b/src/codegen/functions.rs index 8575b1bfd..14cd75a15 100644 --- a/src/codegen/functions.rs +++ b/src/codegen/functions.rs @@ -24,7 +24,7 @@ pub fn generate(env: &Env, root_path: &Path, mod_rs: &mut Vec) { mod_rs.push("\npub mod functions;".into()); for func_analysis in &functions.functions { - function::generate(w, env, func_analysis, false, false, 0)?; + function::generate(w, env, func_analysis, None, false, false, 0)?; } Ok(()) diff --git a/src/codegen/mod.rs b/src/codegen/mod.rs index 36bf85973..63032106b 100644 --- a/src/codegen/mod.rs +++ b/src/codegen/mod.rs @@ -21,6 +21,7 @@ mod records; mod return_value; mod signal; mod signal_body; +mod special_functions; mod sys; mod trait_impls; mod trampoline; diff --git a/src/codegen/object.rs b/src/codegen/object.rs index a3dc98ab3..7191d75f5 100644 --- a/src/codegen/object.rs +++ b/src/codegen/object.rs @@ -37,12 +37,28 @@ pub fn generate( writeln!(w)?; write!(w, "impl {} {{", analysis.name)?; for func_analysis in &analysis.constructors() { - function::generate(w, env, func_analysis, false, false, 1)?; + function::generate( + w, + env, + func_analysis, + Some(&analysis.specials), + false, + false, + 1, + )?; } if !need_generate_trait(analysis) { for func_analysis in &analysis.methods() { - function::generate(w, env, func_analysis, false, false, 1)?; + function::generate( + w, + env, + func_analysis, + Some(&analysis.specials), + false, + false, + 1, + )?; } for property in &analysis.properties { @@ -55,7 +71,15 @@ pub fn generate( } for func_analysis in &analysis.functions() { - function::generate(w, env, func_analysis, false, false, 1)?; + function::generate( + w, + env, + func_analysis, + Some(&analysis.specials), + false, + false, + 1, + )?; } if !need_generate_trait(analysis) { @@ -134,7 +158,7 @@ pub fn generate( generate_trait(w, env, analysis)?; } - if generate_display_trait && !analysis.specials.contains_key(&Type::Display) { + if generate_display_trait && !analysis.specials.has_trait(Type::Display) { writeln!(w, "\nimpl fmt::Display for {} {{", analysis.name,)?; // Generate Display trait implementation. writeln!( @@ -275,7 +299,15 @@ fn generate_trait(w: &mut dyn Write, env: &Env, analysis: &analysis::object::Inf write!(w, "pub trait {}: 'static {{", analysis.trait_name)?; for func_analysis in &analysis.methods() { - function::generate(w, env, func_analysis, true, true, 1)?; + function::generate( + w, + env, + func_analysis, + Some(&analysis.specials), + true, + true, + 1, + )?; } for property in &analysis.properties { properties::generate(w, env, property, true, true, 1)?; @@ -300,7 +332,15 @@ fn generate_trait(w: &mut dyn Write, env: &Env, analysis: &analysis::object::Inf )?; for func_analysis in &analysis.methods() { - function::generate(w, env, func_analysis, true, false, 1)?; + function::generate( + w, + env, + func_analysis, + Some(&analysis.specials), + true, + false, + 1, + )?; } for property in &analysis.properties { properties::generate(w, env, property, true, false, 1)?; diff --git a/src/codegen/record.rs b/src/codegen/record.rs index a310f8aaf..caadae155 100644 --- a/src/codegen/record.rs +++ b/src/codegen/record.rs @@ -31,8 +31,8 @@ pub fn generate(w: &mut dyn Write, env: &Env, analysis: &analysis::record::Info) ); } } else if let (Some(ref_fn), Some(unref_fn)) = ( - analysis.specials.get(&Type::Ref), - analysis.specials.get(&Type::Unref), + analysis.specials.traits().get(&Type::Ref), + analysis.specials.traits().get(&Type::Unref), ) { general::define_shared_type( w, @@ -51,8 +51,8 @@ pub fn generate(w: &mut dyn Write, env: &Env, analysis: &analysis::record::Info) &analysis.derives, )?; } else if let (Some(copy_fn), Some(free_fn)) = ( - analysis.specials.get(&Type::Copy), - analysis.specials.get(&Type::Free), + analysis.specials.traits().get(&Type::Copy), + analysis.specials.traits().get(&Type::Free), ) { general::define_boxed_type( w, @@ -100,7 +100,15 @@ pub fn generate(w: &mut dyn Write, env: &Env, analysis: &analysis::record::Info) write!(w, "impl {} {{", analysis.name)?; for func_analysis in &analysis.functions { - function::generate(w, env, func_analysis, false, false, 1)?; + function::generate( + w, + env, + func_analysis, + Some(&analysis.specials), + false, + false, + 1, + )?; } writeln!(w, "}}")?; diff --git a/src/codegen/special_functions.rs b/src/codegen/special_functions.rs new file mode 100644 index 000000000..8aa0e2cc8 --- /dev/null +++ b/src/codegen/special_functions.rs @@ -0,0 +1,60 @@ +use std::io::{Result, Write}; + +use crate::{ + analysis::{self, functions::Visibility, special_functions::FunctionType}, + Env, +}; + +use super::general::version_condition; + +pub(super) fn generate( + w: &mut dyn Write, + env: &Env, + function: &analysis::functions::Info, + specials: &analysis::special_functions::Infos, +) -> Result { + if let Some(special) = specials.functions().get(&function.glib_name) { + match special.type_ { + FunctionType::StaticStringify => generate_static_to_str(w, env, function), + } + .map(|()| true) + } else { + Ok(false) + } +} + +pub(super) fn generate_static_to_str( + w: &mut dyn Write, + env: &Env, + function: &analysis::functions::Info, +) -> Result<()> { + writeln!(w)?; + version_condition(w, env, function.version, false, 1)?; + + let visibility = match function.visibility { + Visibility::Public => "pub ", + _ => "", + }; + + writeln!( + w, + "\ +\t{visibility}fn {rust_fn_name}<'a>(self) -> &'a str {{ +\t\tunsafe {{ +\t\t\tCStr::from_ptr( +\t\t\t\t{ns}::{glib_fn_name}(self.to_glib()) +\t\t\t\t\t.as_ref() +\t\t\t\t\t.expect(\"{glib_fn_name} returned NULL\"), +\t\t\t) +\t\t\t.to_str() +\t\t\t.expect(\"{glib_fn_name} returned an invalid string\") +\t\t}} +\t}}", + visibility = visibility, + rust_fn_name = function.name, + ns = env.main_sys_crate_name(), + glib_fn_name = function.glib_name, + )?; + + Ok(()) +} diff --git a/src/codegen/trait_impls.rs b/src/codegen/trait_impls.rs index d2735c29e..7a8a734fe 100644 --- a/src/codegen/trait_impls.rs +++ b/src/codegen/trait_impls.rs @@ -16,11 +16,11 @@ pub fn generate( specials: &Infos, trait_name: Option<&str>, ) -> Result<()> { - for (type_, special_info) in specials.iter() { + for (type_, special_info) in specials.traits().iter() { if let Some(info) = lookup(functions, &special_info.glib_name) { match *type_ { Type::Compare => { - if !specials.contains_key(&Type::Equal) { + if !specials.has_trait(Type::Equal) { generate_eq_compare(w, env, type_name, info, trait_name)?; } generate_ord(w, env, type_name, info, trait_name)?; From 5b32e695f97e498a9a3c4c01cb3009ed4b10f890 Mon Sep 17 00:00:00 2001 From: Marijn Suijten Date: Sun, 6 Dec 2020 17:09:58 +0100 Subject: [PATCH 25/26] Give more string-returning functions on flags/enums a static lifetime --- src/analysis/special_functions.rs | 103 +++++++++++++++++------------- 1 file changed, 60 insertions(+), 43 deletions(-) diff --git a/src/analysis/special_functions.rs b/src/analysis/special_functions.rs index b09665c80..3f7aaca06 100644 --- a/src/analysis/special_functions.rs +++ b/src/analysis/special_functions.rs @@ -33,7 +33,6 @@ impl FromStr for Type { "free" | "destroy" => Ok(Free), "is_equal" => Ok(Equal), "ref" | "ref_" => Ok(Ref), - "to_string" | "to_str" | "name" | "get_name" => Ok(Display), "unref" => Ok(Unref), "hash" => Ok(Hash), _ => Err(format!("Unknown type '{}'", s)), @@ -86,43 +85,46 @@ impl Infos { } } -fn update_func(func: &mut FuncInfo, type_: Type, parent_type: &LibType, obj: &GObject) -> bool { - if func.visibility != Visibility::Comment { - func.visibility = visibility(type_); +/// Returns true on functions that take an instance as single argument and +/// return a string as result. +fn is_stringify(func: &mut FuncInfo, parent_type: &LibType, obj: &GObject) -> bool { + if func.parameters.c_parameters.len() != 1 { + return false; + } + if !func.parameters.c_parameters[0].instance_parameter { + return false; } - if type_ == Type::Display { - if func.parameters.c_parameters.len() != 1 { - return false; - } - if !func.parameters.c_parameters[0].instance_parameter { + if let Some(ret) = func.ret.parameter.as_mut() { + if ret.typ != TypeId::tid_utf8() { return false; } - if let Some(ret) = func.ret.parameter.as_mut() { - if ret.typ != TypeId::tid_utf8() { - return false; - } + if func.name == "to_string" { + // Rename to to_str to make sure it doesn't clash with ToString::to_string + func.name = "to_str".to_owned(); - if func.name == "to_string" { - // Rename to to_str to make sure it doesn't clash with ToString::to_string - func.name = "to_str".to_owned(); - - // As to not change old code behaviour, assume non-nullability outside - // enums and flags only, and exclusively for to_string. Function inside - // enums and flags have been appropriately marked in Gir. - if !obj.trust_return_value_nullability - && !matches!(parent_type, LibType::Enumeration(_) | LibType::Bitfield(_)) - { - *ret.nullable = false; - } + // As to not change old code behaviour, assume non-nullability outside + // enums and flags only, and exclusively for to_string. Function inside + // enums and flags have been appropriately marked in Gir. + if !obj.trust_return_value_nullability + && !matches!(parent_type, LibType::Enumeration(_) | LibType::Bitfield(_)) + { + *ret.nullable = false; } - - // Cannot generate Display implementation for Option<> - return !*ret.nullable; } + + // Cannot generate Display implementation for Option<> + !*ret.nullable + } else { + false } +} +fn update_func(func: &mut FuncInfo, type_: Type) -> bool { + if func.visibility != Visibility::Comment { + func.visibility = visibility(type_); + } true } @@ -133,20 +135,7 @@ pub fn extract(functions: &mut Vec, parent_type: &LibType, obj: &GObje let mut destroy = None; for (pos, func) in functions.iter_mut().enumerate() { - if let Ok(type_) = func.name.parse() { - if func.name == "destroy" { - destroy = Some((func.glib_name.clone(), pos)); - continue; - } - if !update_func(func, type_, parent_type, obj) { - continue; - } - if func.name == "copy" { - has_copy = true; - } else if func.name == "free" { - has_free = true; - } - + if is_stringify(func, parent_type, obj) { let return_transfer_none = func .ret .parameter @@ -172,6 +161,34 @@ pub fn extract(functions: &mut Vec, parent_type: &LibType, obj: &GObje ); } + // Some stringifying functions can serve as Display implementation + if matches!( + func.name.as_str(), + "to_string" | "to_str" | "name" | "get_name" + ) { + // FUTURE: Decide which function gets precedence if multiple Display prospects exist. + specials.traits.insert( + Type::Display, + TraitInfo { + glib_name: func.glib_name.clone(), + version: func.version, + }, + ); + } + } else if let Ok(type_) = func.name.parse() { + if func.name == "destroy" { + destroy = Some((func.glib_name.clone(), pos)); + continue; + } + if !update_func(func, type_) { + continue; + } + if func.name == "copy" { + has_copy = true; + } else if func.name == "free" { + has_free = true; + } + specials.traits.insert( type_, TraitInfo { @@ -186,7 +203,7 @@ pub fn extract(functions: &mut Vec, parent_type: &LibType, obj: &GObje if let Some((glib_name, pos)) = destroy { let ty_ = Type::from_str("destroy").unwrap(); let func = &mut functions[pos]; - update_func(func, ty_, parent_type, obj); + update_func(func, ty_); specials.traits.insert( ty_, TraitInfo { From 2915bde468de0785bc2b650444f50c5c56a40e31 Mon Sep 17 00:00:00 2001 From: Marijn Suijten Date: Sun, 6 Dec 2020 16:59:02 +0100 Subject: [PATCH 26/26] Cargo update --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e7f61888a..03715756a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -154,9 +154,9 @@ source = "git+https://github.com/GuillaumeGomez/rustdoc-stripper#bbdf0a350a85c8d [[package]] name = "serde" -version = "1.0.117" +version = "1.0.118" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b88fa983de7720629c9387e9f517353ed404164b1e482c970a90c1a4aaf7dc1a" +checksum = "06c64263859d87aa2eb554587e2d23183398d617427327cf2b3d0ed8c69e4800" [[package]] name = "thread_local"