Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
92 changes: 59 additions & 33 deletions crates/webidl2wit/src/translations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,22 @@ fn handle_unsupported(
}
}

/// A parent named in an inheritance clause has no definition in the input.
fn unknown_parent(
child: &Ident,
parent: &Ident,
handle_unsupported: &HandleUnsupported,
) -> anyhow::Result<()> {
match handle_unsupported {
HandleUnsupported::Bail => bail!("Parent {parent} of {child} not found"),
HandleUnsupported::Skip => Ok(()),
HandleUnsupported::Warn => {
eprintln!("WARN: Skipping unknown parent {parent} of {child}");
Ok(())
}
}
}

pub fn webidl_to_wit(
mut webidl: WebIdlDefinitions,
options: ConversionOptions,
Expand Down Expand Up @@ -404,8 +420,8 @@ pub fn webidl_to_wit(
}
}

state.handle_resource_inheritance().unwrap();
state.handle_record_inheritance().unwrap();
state.handle_resource_inheritance()?;
state.handle_record_inheritance()?;

params_resources_borrow(&mut state.interface, &resource_names);

Expand Down Expand Up @@ -960,6 +976,9 @@ impl State<'_> {
}
}

let unsupported_features = self.unsupported_features.clone();
let resource_inheritance = self.resource_inheritance.clone();

for (child, parents) in ancestors_map {
let child_funcs = self
.interface
Expand All @@ -981,28 +1000,29 @@ impl State<'_> {
}
}
})
.unwrap();
.with_context(|| format!("Resource {child} not found"))?;
let mut func_names: HashSet<String> = child_funcs
.iter()
.map(|f| func_name(f).to_string())
.collect();
for parent in &parents {
let Some(parent_funcs) = parents_funcs.get(parent) else {
unknown_parent(&child, parent, &unsupported_features)?;
continue;
};
if matches!(
self.resource_inheritance,
resource_inheritance,
ResourceInheritance::DuplicateMethods | ResourceInheritance::Both
) {
for parent_func in parents_funcs
.get(parent)
.context(format!("Parent {parent} not found"))?
{
for parent_func in parent_funcs {
if !func_names.contains(func_name(parent_func)) {
func_names.insert(func_name(parent_func).to_string());
child_funcs.push(parent_func.clone());
}
}
}
if matches!(
self.resource_inheritance,
resource_inheritance,
ResourceInheritance::AsMethods | ResourceInheritance::Both
) {
child_funcs.push({
Expand All @@ -1017,31 +1037,34 @@ impl State<'_> {
}

if matches!(
self.resource_inheritance,
resource_inheritance,
ResourceInheritance::AsMethods | ResourceInheritance::Both
) {
for parent in parents {
let parent_funcs = self
.interface
.items_mut()
.into_iter()
.find_map(|item| match item {
InterfaceItem::Function(_) => None,
InterfaceItem::TypeDef(type_def) => {
let name = type_def.name().clone();
match type_def.kind_mut() {
wit_encoder::TypeDefKind::Resource(resource) => {
if name == parent {
Some(resource.funcs_mut())
} else {
None
let Some(parent_funcs) =
self.interface
.items_mut()
.into_iter()
.find_map(|item| match item {
InterfaceItem::Function(_) => None,
InterfaceItem::TypeDef(type_def) => {
let name = type_def.name().clone();
match type_def.kind_mut() {
wit_encoder::TypeDefKind::Resource(resource) => {
if name == parent {
Some(resource.funcs_mut())
} else {
None
}
}
_ => None,
}
_ => None,
}
}
})
.unwrap();
})
else {
// Already reported while merging the child's methods.
continue;
};
parent_funcs.push({
let mut func = wit_encoder::ResourceFunc::method(
format!("as-{}", child.raw_name()),
Expand Down Expand Up @@ -1083,6 +1106,8 @@ impl State<'_> {
let ancestors_map: BTreeMap<Ident, Vec<Ident>> =
resolve_ancestors(&mut self.record_inheritance_map);

let unsupported_features = self.unsupported_features.clone();

for (child, parents) in ancestors_map {
let child_fields = self
.interface
Expand All @@ -1104,14 +1129,15 @@ impl State<'_> {
}
}
})
.unwrap();
.with_context(|| format!("Record {child} not found"))?;
let mut field_names: HashSet<Ident> =
child_fields.iter().map(|f| f.name().clone()).collect();
for parent in &parents {
for parent_field in parents_fields
.get(parent)
.context(format!("Parent {parent} not found"))?
{
let Some(parent_fields) = parents_fields.get(parent) else {
unknown_parent(&child, parent, &unsupported_features)?;
continue;
};
for parent_field in parent_fields {
if !field_names.contains(parent_field.name()) {
field_names.insert(parent_field.name().clone());
child_fields.push(parent_field.clone());
Expand Down
8 changes: 8 additions & 0 deletions crates/webidl2wit/tests/inputs/unknown-parent.idl
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// The parents are declared elsewhere (another spec) and absent from the input.
interface Child : Missing {
undefined fromChild();
};

dictionary ChildDict : MissingDict {
required short fromChild;
};
10 changes: 10 additions & 0 deletions crates/webidl2wit/tests/inputs/unknown-parent.wit
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package webidl:my-package-idl;

interface my-interface {
resource child {
from-child: func();
}
record child-dict {
from-child: s16,
}
}
20 changes: 20 additions & 0 deletions crates/webidl2wit/tests/smoke.rs
Original file line number Diff line number Diff line change
Expand Up @@ -165,3 +165,23 @@ fn html_element() {
},
);
}

#[test]
fn unknown_parent() {
compare(
"unknown-parent",
ConversionOptions {
unsupported_features: HandleUnsupported::Warn,
resource_inheritance: ResourceInheritance::Both,
..Default::default()
},
);
}

#[test]
fn unknown_parent_is_an_error_when_bailing() {
let webidl_input = fs::read_to_string("./tests/inputs/unknown-parent.idl").unwrap();
let webidl_ast = weedle::parse(&webidl_input).unwrap();
let result = webidl2wit::webidl_to_wit(webidl_ast, Default::default());
assert!(result.is_err());
}