Skip to content

Commit 4620f19

Browse files
authored
Merge pull request gtk-rs#694 from sdroege/final-types
Implement configuration/detection for final types and use that inform…
2 parents 2f0a317 + 122dcbf commit 4620f19

File tree

9 files changed

+105
-52
lines changed

9 files changed

+105
-52
lines changed

README.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -136,8 +136,10 @@ name = "Gtk.SomeClass"
136136
status = "generate"
137137
# replace the parameter name for the child in child properties (instead "child")
138138
child_name = "item"
139-
# don't generate trait SomeClassExt for this object, but implement all functions in impl SomeClass
140-
trait = false
139+
# mark object as final type, i.e. one without any further subclasses. this
140+
# will not generate trait SomeClassExt for this object, but implement all
141+
# functions in impl SomeClass
142+
final_type = true
141143
# allow rename result file
142144
module_name = "soome_class"
143145
# override starting version

src/analysis/bounds.rs

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use analysis::out_parameters::use_function_return_for_result;
99
use analysis::rust_type::{bounds_rust_type, rust_type};
1010
use consts::TYPE_PARAMETERS_START;
1111
use env::Env;
12-
use library::{Function, Fundamental, Nullable, ParameterDirection, Type, TypeId};
12+
use library::{Class, Function, Fundamental, Nullable, ParameterDirection, Type, TypeId};
1313
use traits::IntoString;
1414

1515
#[derive(Clone, Eq, Debug, PartialEq)]
@@ -177,8 +177,20 @@ impl Bounds {
177177
Type::Fundamental(Fundamental::Filename) => Some(AsRef(None)),
178178
Type::Fundamental(Fundamental::OsString)=> Some(AsRef(None)),
179179
Type::Fundamental(Fundamental::Utf8) if *nullable => Some(Into(Some('_'), None)),
180-
Type::Class(..) if !*nullable => Some(IsA(None)),
181-
Type::Class(..) => Some(Into(Some('_'), Some(Box::new(IsA(None))))),
180+
Type::Class(Class { final_type, ..}) if !*nullable => {
181+
if final_type {
182+
None
183+
} else {
184+
Some(IsA(None))
185+
}
186+
},
187+
Type::Class(Class { final_type, ..}) => {
188+
if final_type {
189+
Some(Into(Some('_'), None))
190+
} else {
191+
Some(Into(Some('_'), Some(Box::new(IsA(None)))))
192+
}
193+
},
182194
Type::Interface(..) if !*nullable => Some(IsA(None)),
183195
Type::Interface(..) => Some(Into(Some('_'), Some(Box::new(IsA(None))))),
184196
Type::List(_) | Type::SList(_) | Type::CArray(_) => None,

src/analysis/object.rs

Lines changed: 11 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ pub struct Info {
2020
pub get_type: String,
2121
pub is_interface: bool,
2222
pub supertypes: Vec<general::StatusedTypeId>,
23+
pub final_type: bool,
2324
pub generate_trait: bool,
2425
pub trait_name: String,
2526
pub has_constructors: bool,
@@ -54,22 +55,6 @@ impl Deref for Info {
5455
}
5556
}
5657

57-
pub fn has_known_subtypes(env: &Env, class_tid: library::TypeId) -> bool {
58-
for child_tid in env.class_hierarchy.subtypes(class_tid) {
59-
let child_name = child_tid.full_name(&env.library);
60-
let status = env.config
61-
.objects
62-
.get(&child_name)
63-
.map(|o| o.status)
64-
.unwrap_or_default();
65-
if status.normal() {
66-
return true;
67-
}
68-
}
69-
70-
false
71-
}
72-
7358
pub fn class(env: &Env, obj: &GObject, deps: &[library::TypeId]) -> Option<Info> {
7459
info!("Analyzing class {}", obj.name);
7560
let full_name = obj.name.clone();
@@ -97,21 +82,12 @@ pub fn class(env: &Env, obj: &GObject, deps: &[library::TypeId]) -> Option<Info>
9782

9883
let supertypes = supertypes::analyze(env, class_tid, &mut imports);
9984

100-
let mut generate_trait = obj.generate_trait;
85+
let final_type = klass.final_type;
10186
let trait_name = obj.trait_name
10287
.as_ref()
10388
.cloned()
10489
.unwrap_or_else(|| format!("{}Ext", name));
10590

106-
// Sanity check the user's configuration. It's unlikely that not generating
107-
// a trait is wanted if there are subtypes in this very crate
108-
if !generate_trait && has_known_subtypes(env, class_tid) {
109-
error!(
110-
"Not generating trait for {} although subtypes exist",
111-
full_name
112-
);
113-
}
114-
11591
let mut trampolines =
11692
trampolines::Trampolines::with_capacity(klass.signals.len() + klass.properties.len());
11793
let mut signatures = Signatures::with_capacity(klass.functions.len());
@@ -120,7 +96,7 @@ pub fn class(env: &Env, obj: &GObject, deps: &[library::TypeId]) -> Option<Info>
12096
env,
12197
&klass.functions,
12298
class_tid,
123-
generate_trait,
99+
!final_type,
124100
obj,
125101
&mut imports,
126102
Some(&mut signatures),
@@ -145,7 +121,7 @@ pub fn class(env: &Env, obj: &GObject, deps: &[library::TypeId]) -> Option<Info>
145121
env,
146122
&klass.signals,
147123
class_tid,
148-
generate_trait,
124+
!final_type,
149125
&mut trampolines,
150126
obj,
151127
&mut imports,
@@ -154,7 +130,7 @@ pub fn class(env: &Env, obj: &GObject, deps: &[library::TypeId]) -> Option<Info>
154130
env,
155131
&klass.properties,
156132
class_tid,
157-
generate_trait,
133+
!final_type,
158134
&mut trampolines,
159135
obj,
160136
&mut imports,
@@ -181,15 +157,11 @@ pub fn class(env: &Env, obj: &GObject, deps: &[library::TypeId]) -> Option<Info>
181157

182158
// There's no point in generating a trait if there are no signals, methods, properties
183159
// and child properties: it would be empty
184-
if generate_trait && !has_signals && !has_methods && properties.is_empty()
185-
&& child_properties.is_empty()
186-
{
187-
generate_trait = false;
188-
}
160+
//
161+
// There's also no point in generating a trait for final types: there are no possible subtypes
162+
let generate_trait = !final_type && (has_signals || has_methods || !properties.is_empty() || !child_properties.is_empty());
189163

190-
if generate_trait
191-
&& (has_methods || !properties.is_empty() || !child_properties.is_empty() || has_signals)
192-
{
164+
if generate_trait {
193165
imports.add("glib::object::IsA", None);
194166
}
195167

@@ -233,6 +205,7 @@ pub fn class(env: &Env, obj: &GObject, deps: &[library::TypeId]) -> Option<Info>
233205
get_type: klass.glib_get_type.clone(),
234206
is_interface: false,
235207
supertypes,
208+
final_type,
236209
generate_trait,
237210
trait_name,
238211
has_constructors,
@@ -353,6 +326,7 @@ pub fn interface(env: &Env, obj: &GObject, deps: &[library::TypeId]) -> Option<I
353326
get_type: iface.glib_get_type.clone(),
354327
is_interface: true,
355328
supertypes,
329+
final_type: false,
356330
generate_trait: true,
357331
trait_name,
358332
has_methods,

src/codegen/object.rs

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -133,8 +133,10 @@ pub fn generate(
133133
try!(writeln!(w, "unsafe impl Sync for {} {{}}", analysis.name));
134134
}
135135

136-
try!(writeln!(w));
137-
try!(writeln!(w, "pub const NONE_{}: Option<&{}> = None;", analysis.name.to_snake().to_uppercase(), analysis.name));
136+
if !analysis.final_type {
137+
try!(writeln!(w));
138+
try!(writeln!(w, "pub const NONE_{}: Option<&{}> = None;", analysis.name.to_snake().to_uppercase(), analysis.name));
139+
}
138140

139141
if need_generate_trait(analysis) {
140142
try!(writeln!(w));
@@ -279,10 +281,17 @@ pub fn generate_reexports(
279281
contents.extend_from_slice(&cfgs);
280282
contents.push(format!("mod {};", module_name));
281283
contents.extend_from_slice(&cfgs);
284+
285+
let none_type = if !analysis.final_type {
286+
format!(", NONE_{}", analysis.name.to_snake().to_uppercase())
287+
} else {
288+
String::new()
289+
};
290+
282291
if let Some(ref class_name) = analysis.rust_class_type {
283-
contents.push(format!("pub use self::{}::{{{}, {}, {}}};", module_name, analysis.name, class_name, format!("NONE_{}", analysis.name.to_snake().to_uppercase())));
292+
contents.push(format!("pub use self::{}::{{{}, {}{}}};", module_name, analysis.name, class_name, none_type));
284293
} else {
285-
contents.push(format!("pub use self::{}::{{{}, {}}};", module_name, analysis.name, format!("NONE_{}", analysis.name.to_snake().to_uppercase())));
294+
contents.push(format!("pub use self::{}::{{{}{}}};", module_name, analysis.name, none_type));
286295
}
287296
if need_generate_trait(analysis) {
288297
contents.extend_from_slice(&cfgs);

src/config/gobjects.rs

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ pub struct GObject {
7070
pub version: Option<Version>,
7171
pub cfg_condition: Option<String>,
7272
pub type_id: Option<TypeId>,
73-
pub generate_trait: bool,
73+
pub final_type: Option<bool>,
7474
pub trait_name: Option<String>,
7575
pub child_properties: Option<ChildProperties>,
7676
pub concurrency: library::Concurrency,
@@ -97,7 +97,7 @@ impl Default for GObject {
9797
version: None,
9898
cfg_condition: None,
9999
type_id: None,
100-
generate_trait: true,
100+
final_type: None,
101101
trait_name: None,
102102
child_properties: None,
103103
concurrency: Default::default(),
@@ -183,6 +183,7 @@ fn parse_object(
183183
"child_prop",
184184
"child_name",
185185
"child_type",
186+
"final_type",
186187
"trait",
187188
"trait_name",
188189
"cfg_condition",
@@ -236,8 +237,11 @@ fn parse_object(
236237
.map(|s| s.to_owned());
237238
let generate_trait = toml_object
238239
.lookup("trait")
240+
.and_then(|v| v.as_bool());
241+
let final_type = toml_object
242+
.lookup("final_type")
239243
.and_then(|v| v.as_bool())
240-
.unwrap_or(true);
244+
.or_else(|| generate_trait.map(|t| !t));
241245
let trait_name = toml_object
242246
.lookup("trait_name")
243247
.and_then(|v| v.as_str())
@@ -281,6 +285,10 @@ fn parse_object(
281285
warn!("conversion_type configuration used for non-manual object {}", name);
282286
}
283287

288+
if generate_trait.is_some() {
289+
warn!("`trait` configuration is deprecated and replaced by `final_type` for object {}", name);
290+
}
291+
284292
GObject {
285293
name,
286294
functions,
@@ -294,7 +302,7 @@ fn parse_object(
294302
version,
295303
cfg_condition,
296304
type_id: None,
297-
generate_trait,
305+
final_type,
298306
trait_name,
299307
child_properties,
300308
concurrency,

src/library.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -466,6 +466,7 @@ pub struct Class {
466466
pub properties: Vec<Property>,
467467
pub parent: Option<TypeId>,
468468
pub implements: Vec<TypeId>,
469+
pub final_type: bool,
469470
pub version: Option<Version>,
470471
pub deprecated_version: Option<Version>,
471472
pub doc: Option<String>,

src/library_postprocessing.rs

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ use library::*;
55
use nameutil;
66
use parser::is_empty_c_type;
77
use traits::MaybeRefAs;
8+
use config::Config;
9+
use config::gobjects::GObject;
810

911
impl Namespace {
1012
fn unresolved(&self) -> Vec<&str> {
@@ -22,14 +24,15 @@ impl Namespace {
2224
type DetectedCTypes = HashMap<TypeId, String>;
2325

2426
impl Library {
25-
pub fn postprocessing(&mut self) {
27+
pub fn postprocessing(&mut self, config: &Config) {
2628
self.fix_gtype();
2729
self.check_resolved();
2830
self.fill_empty_signals_c_types();
2931
self.resolve_class_structs();
3032
self.correlate_class_structs();
3133
self.fix_fields();
3234
self.make_unrepresentable_types_opaque();
35+
self.mark_final_types(config);
3336
}
3437

3538
fn fix_gtype(&mut self) {
@@ -373,4 +376,47 @@ impl Library {
373376
}
374377
}
375378
}
379+
380+
fn mark_final_types(&mut self, config: &Config) {
381+
// Here we mark all class types as final types if configured so in the config or
382+
// otherwise if there is no public class struct for the type.
383+
//
384+
// Final types can't have any subclasses and we handle them slightly different
385+
// for that reason.
386+
let mut final_types: Vec<TypeId> = Vec::new();
387+
388+
for (ns_id, ns) in self.namespaces.iter().enumerate() {
389+
for (id, type_) in ns.types.iter().enumerate() {
390+
let type_ = type_.as_ref().unwrap(); //Always contains something
391+
392+
if let Type::Class(ref klass) = *type_ {
393+
let tid = TypeId {
394+
ns_id: ns_id as u16,
395+
id: id as u32,
396+
};
397+
398+
let full_name = tid.full_name(self);
399+
let obj = config.objects.get(&*full_name);
400+
401+
if let Some(GObject { final_type: Some(final_type), ..}) = obj {
402+
// The config might also be used to override a type that is wrongly
403+
// detected as final type otherwise
404+
if *final_type {
405+
final_types.push(tid);
406+
}
407+
} else if klass.c_class_type.is_none() {
408+
final_types.push(tid);
409+
}
410+
}
411+
}
412+
}
413+
414+
for tid in final_types {
415+
if let Type::Class(Class { ref mut final_type, ..}) = *self.type_mut(tid) {
416+
*final_type = true;
417+
} else {
418+
unreachable!();
419+
}
420+
}
421+
}
376422
}

src/main.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ fn do_main() -> Result<(), String> {
9494

9595
{
9696
let _watcher = statistics.enter("Postprocessing");
97-
library.postprocessing();
97+
library.postprocessing(&cfg);
9898
}
9999

100100
{

src/parser.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,7 @@ impl Library {
198198
properties,
199199
parent,
200200
implements: impls,
201+
final_type: false, // this will be set during postprocessing
201202
doc,
202203
version,
203204
deprecated_version,

0 commit comments

Comments
 (0)