Fix panic when exporting invalid types

And also still allow to export builtin component
This commit is contained in:
Olivier Goffart 2020-11-02 17:16:51 +01:00
parent f28d6f88c2
commit decbe0ade5
4 changed files with 78 additions and 45 deletions

View file

@ -88,7 +88,7 @@ impl Document {
_ => {} _ => {}
}; };
} }
let exports = Exports::from_node(&node, &inner_components, &parent_registry); let exports = Exports::from_node(&node, &inner_components, &local_registry, diag);
Document { Document {
// FIXME: one should use the `component` hint instead of always returning the last // FIXME: one should use the `component` hint instead of always returning the last
@ -1023,12 +1023,6 @@ pub struct Transition {
pub property_animations: Vec<(NamedReference, ElementRc)>, pub property_animations: Vec<(NamedReference, ElementRc)>,
} }
#[derive(Debug, Clone)]
pub struct NamedExport {
pub internal_name: String,
pub exported_name: String,
}
#[derive(Default, Debug, derive_more::Deref)] #[derive(Default, Debug, derive_more::Deref)]
pub struct Exports(Vec<(String, Rc<Component>)>); pub struct Exports(Vec<(String, Rc<Component>)>);
@ -1036,8 +1030,16 @@ impl Exports {
pub fn from_node( pub fn from_node(
doc: &syntax_nodes::Document, doc: &syntax_nodes::Document,
inner_components: &Vec<Rc<Component>>, inner_components: &Vec<Rc<Component>>,
type_registry: &Rc<RefCell<TypeRegister>>, type_registry: &TypeRegister,
diag: &mut FileDiagnostics,
) -> Self { ) -> Self {
#[derive(Debug, Clone)]
struct NamedExport {
internal_name_ident: SyntaxNodeWithSourceFile,
internal_name: String,
exported_name: String,
}
let mut exports = doc let mut exports = doc
.ExportsList() .ExportsList()
.flat_map(|exports| exports.ExportSpecifier()) .flat_map(|exports| exports.ExportSpecifier())
@ -1049,7 +1051,11 @@ impl Exports {
.expect("internal error: missing external name for export"), .expect("internal error: missing external name for export"),
None => internal_name.clone(), None => internal_name.clone(),
}; };
Some(NamedExport { internal_name, exported_name }) Some(NamedExport {
internal_name_ident: export_specifier.ExportIdentifier().into(),
internal_name,
exported_name,
})
}) })
.collect::<Vec<_>>(); .collect::<Vec<_>>();
@ -1057,57 +1063,53 @@ impl Exports {
|component| { |component| {
let name = identifier_text(&component.DeclaredIdentifier()) let name = identifier_text(&component.DeclaredIdentifier())
.expect("internal error: cannot export component without name"); .expect("internal error: cannot export component without name");
Some(NamedExport { internal_name: name.clone(), exported_name: name }) Some(NamedExport {
internal_name_ident: component.DeclaredIdentifier().into(),
internal_name: name.clone(),
exported_name: name,
})
}, },
)); ));
if exports.is_empty() { if exports.is_empty() {
let internal_name = inner_components.last().cloned().unwrap_or_default().id.clone(); let internal_name = inner_components.last().cloned().unwrap_or_default().id.clone();
exports.push(NamedExport { exports.push(NamedExport {
internal_name_ident: doc.clone().into(),
internal_name: internal_name.clone(), internal_name: internal_name.clone(),
exported_name: internal_name, exported_name: internal_name,
}) })
} }
let imported_names = doc let mut resolve_export_to_inner_component_or_import =
.ImportSpecifier() |export: &NamedExport| match type_registry.lookup(export.internal_name.as_str()) {
.map(|import| crate::typeloader::ImportedName::extract_imported_names(&import)) Type::Component(c) => Some(c),
.flatten() Type::Invalid => {
.collect::<Vec<_>>(); diag.push_error(
format!("'{}' not found", export.internal_name),
let resolve_export_to_inner_component_or_import = |export: &NamedExport| { &export.internal_name_ident,
if let Some(local_comp) = inner_components.iter().find(|c| c.id == export.internal_name) );
{
local_comp.clone()
} else {
imported_names
.iter()
.find_map(|import| {
if import.internal_name == export.internal_name {
Some(
type_registry
.borrow()
.lookup_element(&import.internal_name)
.unwrap()
.as_component()
.clone(),
)
} else {
None None
} }
}) _ => {
.unwrap() diag.push_error(
format!(
"Cannot export '{}' because it is not a component",
export.internal_name,
),
&export.internal_name_ident,
);
None
} }
}; };
Self( Self(
exports exports
.iter() .iter()
.map(|export| { .filter_map(|export| {
( Some((
export.exported_name.clone(), export.exported_name.clone(),
resolve_export_to_inner_component_or_import(export), resolve_export_to_inner_component_or_import(export)?,
) ))
}) })
.collect(), .collect(),
) )

View file

@ -597,7 +597,10 @@ fn parse_export(p: &mut impl Parser) -> bool {
p.consume(); p.consume();
return true; return true;
} }
SyntaxKind::Eof => return false, SyntaxKind::Eof => {
p.error("Expected comma");
return false;
}
SyntaxKind::Comma => { SyntaxKind::Comma => {
p.consume(); p.consume();
} }

View file

@ -0,0 +1,22 @@
/* LICENSE BEGIN
This file is part of the SixtyFPS Project -- https://sixtyfps.io
Copyright (c) 2020 Olivier Goffart <olivier.goffart@sixtyfps.io>
Copyright (c) 2020 Simon Hausmann <simon.hausmann@sixtyfps.io>
SPDX-License-Identifier: GPL-3.0-only
This file is also available under commercial licensing terms.
Please contact info@sixtyfps.io for more information.
LICENSE END */
export { Foo as Bar }
// ^error{'Foo' not found}
export { Image as Plop }
// ^error{Cannot export 'Image' because it is not a component}
export { string as Boob }
// ^error{Cannot export 'string' because it is not a component}
Hello := Plop {
// ^error{Unknown type Plop}
}

View file

@ -75,6 +75,7 @@ struct ImportedTypes {
type DependenciesByFile = BTreeMap<PathBuf, ImportedTypes>; type DependenciesByFile = BTreeMap<PathBuf, ImportedTypes>;
#[derive(Debug)]
pub struct ImportedName { pub struct ImportedName {
// name of export to match in the other file // name of export to match in the other file
pub external_name: String, pub external_name: String,
@ -161,6 +162,11 @@ impl<'a> TypeLoader<'a> {
dependency_diagnostics.current_path = SourceFile::new(path.clone()); dependency_diagnostics.current_path = SourceFile::new(path.clone());
if dependency_diagnostics.has_error() {
self.build_diagnostics.add(dependency_diagnostics);
return;
}
let dependency_doc: syntax_nodes::Document = dependency_doc.into(); let dependency_doc: syntax_nodes::Document = dependency_doc.into();
let dependency_registry = let dependency_registry =