Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Generate impl blocks for associated enum functions #991

Merged
merged 26 commits into from
Dec 7, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
bfb0a2d
Fix clippy::search_is_some
MarijnS95 Nov 25, 2020
a639364
Fix clippy::unnecessary_wraps
MarijnS95 Nov 25, 2020
e54e73f
env: Remove unnecessary `if` from main_sys_crate_name
MarijnS95 Nov 22, 2020
18d4a54
special_functions: Perform argument and ret validation on to_string
MarijnS95 Nov 30, 2020
68e4c73
special_functions: Rename ToString after the trait it impls: Display
MarijnS95 Nov 30, 2020
0f554d7
codegen/parameter: Do not pass ByRef::None as reference
MarijnS95 Nov 15, 2020
354c580
Remove unnecessary main_sys_crate_name imports
MarijnS95 Dec 5, 2020
ef8c22b
Generate associated functions for enums
MarijnS95 Aug 29, 2020
9bc5f6e
enum: all imports should go to one Imports as it's one file
MarijnS95 Aug 30, 2020
841582f
Only generate enum functions if >=1 function
MarijnS95 Sep 11, 2020
6e89f56
imports: Add support for multiple, lazily added declared symbols
MarijnS95 Nov 14, 2020
50cee9e
enums: Move rejection and glib imports from codegen to analysis
MarijnS95 Nov 14, 2020
70eab37
codegen/enums: Add missing trait_impls::generate call
MarijnS95 Nov 15, 2020
3023445
analysis/enums: Treat first function parameter as instance
MarijnS95 Nov 15, 2020
7123280
codegen: Add version condition on special function traits
MarijnS95 Nov 15, 2020
c3e827f
enums: Generate special to_str for static functions
MarijnS95 Nov 18, 2020
37366a2
enums: Do not inherit InfoBase to get rid of unused imports
MarijnS95 Nov 21, 2020
6a43595
analysis: Move enum discovery out of generic analyze() function
MarijnS95 Nov 21, 2020
e549975
Copypaste enum analysis and function generation to bitfield
MarijnS95 Nov 21, 2020
518d277
codegen/flags: Add missing display trait generation
MarijnS95 Nov 30, 2020
68760c0
codegen: Don't emit Rust Display impl when C function is available
MarijnS95 Nov 30, 2020
a1cfe65
special_functions: Add version condition to imports as well
MarijnS95 Dec 2, 2020
bf7dd09
special_functions: Only override to_string nullability if not trusted
MarijnS95 Dec 5, 2020
18c12a5
Separate special functions from traits
MarijnS95 Dec 6, 2020
5b32e69
Give more string-returning functions on flags/enums a static lifetime
MarijnS95 Dec 6, 2020
2915bde
Cargo update
MarijnS95 Dec 6, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

123 changes: 123 additions & 0 deletions src/analysis/enums.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
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<functions::Info>,
pub specials: special_functions::Infos,
}

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, imports: &mut Imports) -> Option<Info> {
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()?;

let name = split_namespace_name(&obj.name).1;

// 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,
enumeration_tid,
false,
false,
obj,
imports,
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
sdroege marked this conversation as resolved.
Show resolved Hide resolved
);
}
}
}

let specials = special_functions::extract(&mut functions, type_, obj);

special_functions::analyze_imports(&specials, imports);

let info = Info {
full_name: obj.name.clone(),
type_id: enumeration_tid,
name: name.to_owned(),
functions,
specials,
};

Some(info)
}
117 changes: 117 additions & 0 deletions src/analysis/flags.rs
Original file line number Diff line number Diff line change
@@ -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<functions::Info>,
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> {
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_, obj);

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)
}
5 changes: 1 addition & 4 deletions src/analysis/function_parameters.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
2 changes: 1 addition & 1 deletion src/analysis/functions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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![];
Expand Down
32 changes: 22 additions & 10 deletions src/analysis/imports.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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<String>,
/// Names defined within current module. It doesn't need use declaration.
defined: HashSet<String>,
defaults: ImportConditions,
map: BTreeMap<String, ImportConditions>,
}
Expand All @@ -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(),
}
Expand All @@ -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(),
}
Expand Down Expand Up @@ -120,19 +119,32 @@ 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") || 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`.
Expand Down
Loading