1use crate::diagnostics::{BuildDiagnostics, SourceLocation, Spanned};
11use crate::expression_tree::{self, BindingExpression, Callable, Expression, Unit};
12use crate::langtype::{
13 BuiltinElement, BuiltinPropertyDefault, Enumeration, EnumerationValue, Function, NativeClass,
14 Struct, StructName, Type,
15};
16use crate::langtype::{ElementType, PropertyLookupResult};
17use crate::layout::{LayoutConstraints, Orientation};
18use crate::namedreference::NamedReference;
19use crate::parser;
20use crate::parser::{SyntaxKind, SyntaxNode, syntax_nodes};
21use crate::typeloader::{ImportKind, ImportedTypes, LibraryInfo};
22use crate::typeregister::TypeRegister;
23use itertools::Either;
24use smol_str::{SmolStr, ToSmolStr, format_smolstr};
25use std::cell::{Cell, OnceCell, RefCell};
26use std::collections::btree_map::Entry;
27use std::collections::{BTreeMap, HashMap, HashSet};
28use std::fmt::Display;
29use std::path::PathBuf;
30use std::rc::{Rc, Weak};
31
32macro_rules! unwrap_or_continue {
33 ($e:expr ; $diag:expr) => {
34 match $e {
35 Some(x) => x,
36 None => {
37 debug_assert!($diag.has_errors()); continue;
39 }
40 }
41 };
42}
43
44#[derive(Default)]
46pub struct Document {
47 pub node: Option<syntax_nodes::Document>,
48 pub inner_components: Vec<Rc<Component>>,
49 pub inner_types: Vec<Type>,
50 pub local_registry: TypeRegister,
51 pub custom_fonts: Vec<(SmolStr, crate::parser::SyntaxToken)>,
54 pub exports: Exports,
55 pub imports: Vec<ImportedTypes>,
56 pub library_exports: HashMap<String, LibraryInfo>,
57
58 pub embedded_file_resources:
61 RefCell<BTreeMap<SmolStr, crate::embedded_resources::EmbeddedResources>>,
62
63 #[cfg(feature = "bundle-translations")]
64 pub translation_builder: Option<crate::translations::TranslationsBuilder>,
65
66 pub used_types: RefCell<UsedSubTypes>,
68
69 pub popup_menu_impl: Option<Rc<Component>>,
71}
72
73impl Document {
74 pub fn from_node(
75 node: syntax_nodes::Document,
76 imports: Vec<ImportedTypes>,
77 reexports: Exports,
78 diag: &mut BuildDiagnostics,
79 parent_registry: &Rc<RefCell<TypeRegister>>,
80 ) -> Self {
81 debug_assert_eq!(node.kind(), SyntaxKind::Document);
82
83 let mut local_registry = TypeRegister::new(parent_registry);
84 let mut inner_components = Vec::new();
85 let mut inner_types = Vec::new();
86
87 let mut process_component =
88 |n: syntax_nodes::Component,
89 diag: &mut BuildDiagnostics,
90 local_registry: &mut TypeRegister| {
91 let compo = Component::from_node(n, diag, local_registry);
92 if !local_registry.add(compo.clone()) {
93 diag.push_warning(format!("Component '{}' is replacing a previously defined component with the same name", compo.id), &compo.node.clone().unwrap().DeclaredIdentifier());
94 }
95 inner_components.push(compo);
96 };
97 let process_struct = |n: syntax_nodes::StructDeclaration,
98 diag: &mut BuildDiagnostics,
99 local_registry: &mut TypeRegister,
100 inner_types: &mut Vec<Type>| {
101 let ty = type_struct_from_node(
102 n.ObjectType(),
103 diag,
104 local_registry,
105 parser::identifier_text(&n.DeclaredIdentifier()),
106 );
107 assert!(matches!(ty, Type::Struct(_)));
108 if !local_registry.insert_type(ty.clone()) {
109 diag.push_warning(
110 format!(
111 "Struct '{ty}' is replacing a previously defined type with the same name"
112 ),
113 &n.DeclaredIdentifier(),
114 );
115 }
116 inner_types.push(ty);
117 };
118 let process_enum = |n: syntax_nodes::EnumDeclaration,
119 diag: &mut BuildDiagnostics,
120 local_registry: &mut TypeRegister,
121 inner_types: &mut Vec<Type>| {
122 let Some(name) = parser::identifier_text(&n.DeclaredIdentifier()) else {
123 assert!(diag.has_errors());
124 return;
125 };
126 let mut existing_names = HashSet::new();
127 let values = n
128 .EnumValue()
129 .filter_map(|v| {
130 let value = parser::identifier_text(&v)?;
131 if value == name {
132 diag.push_error(
133 format!("Enum '{value}' can't have a value with the same name"),
134 &v,
135 );
136 None
137 } else if !existing_names.insert(crate::generator::to_pascal_case(&value)) {
138 diag.push_error(format!("Duplicated enum value '{value}'"), &v);
139 None
140 } else {
141 Some(value)
142 }
143 })
144 .collect();
145 let en =
146 Enumeration { name: name.clone(), values, default_value: 0, node: Some(n.clone()) };
147 if en.values.is_empty() {
148 diag.push_error("Enums must have at least one value".into(), &n);
149 }
150
151 let ty = Type::Enumeration(Rc::new(en));
152 if !local_registry.insert_type_with_name(ty.clone(), name.clone()) {
153 diag.push_warning(
154 format!(
155 "Enum '{name}' is replacing a previously defined type with the same name"
156 ),
157 &n.DeclaredIdentifier(),
158 );
159 }
160 inner_types.push(ty);
161 };
162
163 for n in node.children() {
164 match n.kind() {
165 SyntaxKind::Component => process_component(n.into(), diag, &mut local_registry),
166 SyntaxKind::StructDeclaration => {
167 process_struct(n.into(), diag, &mut local_registry, &mut inner_types)
168 }
169 SyntaxKind::EnumDeclaration => {
170 process_enum(n.into(), diag, &mut local_registry, &mut inner_types)
171 }
172 SyntaxKind::ExportsList => {
173 for n in n.children() {
174 match n.kind() {
175 SyntaxKind::Component => {
176 process_component(n.into(), diag, &mut local_registry)
177 }
178 SyntaxKind::StructDeclaration => process_struct(
179 n.into(),
180 diag,
181 &mut local_registry,
182 &mut inner_types,
183 ),
184 SyntaxKind::EnumDeclaration => {
185 process_enum(n.into(), diag, &mut local_registry, &mut inner_types)
186 }
187 _ => {}
188 }
189 }
190 }
191 _ => {}
192 };
193 }
194 let mut exports = Exports::from_node(&node, &inner_components, &local_registry, diag);
195 exports.add_reexports(reexports, diag);
196
197 let custom_fonts = imports
198 .iter()
199 .filter(|import| matches!(import.import_kind, ImportKind::FileImport))
200 .filter_map(|import| {
201 if import.file.ends_with(".ttc")
202 || import.file.ends_with(".ttf")
203 || import.file.ends_with(".otf")
204 {
205 let token_path = import.import_uri_token.source_file.path();
206 let import_file_path = PathBuf::from(import.file.clone());
207 let import_file_path = crate::pathutils::join(token_path, &import_file_path)
208 .unwrap_or(import_file_path);
209
210 if crate::pathutils::is_url(&import_file_path)
213 || crate::fileaccess::load_file(std::path::Path::new(&import_file_path))
214 .is_some()
215 {
216 Some((import_file_path.to_string_lossy().into(), import.import_uri_token.clone()))
217 } else {
218 diag.push_error(
219 format!("File \"{}\" not found", import.file),
220 &import.import_uri_token,
221 );
222 None
223 }
224 } else if import.file.ends_with(".slint") {
225 diag.push_error("Import names are missing. Please specify which types you would like to import".into(), &import.import_uri_token.parent());
226 None
227 } else {
228 diag.push_error(
229 format!("Unsupported foreign import \"{}\"", import.file),
230 &import.import_uri_token,
231 );
232 None
233 }
234 })
235 .collect();
236
237 for local_compo in &inner_components {
238 if exports
239 .components_or_types
240 .iter()
241 .filter_map(|(_, exported_compo_or_type)| exported_compo_or_type.as_ref().left())
242 .any(|exported_compo| Rc::ptr_eq(exported_compo, local_compo))
243 {
244 continue;
245 }
246 if local_compo.is_global() {
249 continue;
250 }
251 if !local_compo.used.get() {
252 diag.push_warning(
253 "Component is neither used nor exported".into(),
254 &local_compo.node.as_ref().map(|n| n.to_source_location()),
255 )
256 }
257 }
258
259 Document {
260 node: Some(node),
261 inner_components,
262 inner_types,
263 local_registry,
264 custom_fonts,
265 imports,
266 exports,
267 library_exports: Default::default(),
268 embedded_file_resources: Default::default(),
269 #[cfg(feature = "bundle-translations")]
270 translation_builder: None,
271 used_types: Default::default(),
272 popup_menu_impl: None,
273 }
274 }
275
276 pub fn exported_roots(&self) -> impl DoubleEndedIterator<Item = Rc<Component>> + '_ {
277 self.exports.iter().filter_map(|e| e.1.as_ref().left()).filter(|c| !c.is_global()).cloned()
278 }
279
280 pub fn last_exported_component(&self) -> Option<Rc<Component>> {
282 self.exports
283 .iter()
284 .filter_map(|e| Some((&e.0.name_ident, e.1.as_ref().left()?)))
285 .filter(|(_, c)| !c.is_global())
286 .max_by_key(|(n, _)| n.text_range().end())
287 .map(|(_, c)| c.clone())
288 }
289
290 pub fn visit_all_used_components(&self, mut v: impl FnMut(&Rc<Component>)) {
292 let used_types = self.used_types.borrow();
293 for c in &used_types.sub_components {
294 v(c);
295 }
296 for c in self.exported_roots() {
297 v(&c);
298 }
299 for c in &used_types.globals {
300 v(c);
301 }
302 if let Some(c) = &self.popup_menu_impl {
303 v(c);
304 }
305 }
306}
307
308#[derive(Debug, Clone)]
309pub struct PopupWindow {
310 pub component: Rc<Component>,
311 pub x: NamedReference,
312 pub y: NamedReference,
313 pub close_policy: EnumerationValue,
314 pub parent_element: ElementRc,
315}
316
317#[derive(Debug, Clone)]
318pub struct Timer {
319 pub interval: NamedReference,
320 pub triggered: NamedReference,
321 pub running: NamedReference,
322 pub element: ElementWeak,
323}
324
325#[derive(Clone, Debug)]
326pub struct ChildrenInsertionPoint {
327 pub parent: ElementRc,
328 pub insertion_index: usize,
329 pub node: syntax_nodes::ChildrenPlaceholder,
330}
331
332#[derive(Debug, Default)]
334pub struct UsedSubTypes {
335 pub globals: Vec<Rc<Component>>,
337 pub structs_and_enums: Vec<Type>,
339 pub sub_components: Vec<Rc<Component>>,
342 pub library_types_imports: Vec<(SmolStr, LibraryInfo)>,
345 pub library_global_imports: Vec<(SmolStr, LibraryInfo)>,
348}
349
350#[derive(Clone, Debug)]
352struct UsesStatement {
353 interface_name: QualifiedTypeName,
354 child_id: SmolStr,
355 node: syntax_nodes::UsesIdentifier,
356}
357
358impl UsesStatement {
359 fn interface_name_node(&self) -> syntax_nodes::QualifiedName {
361 self.node.QualifiedName()
362 }
363
364 fn child_id_node(&self) -> syntax_nodes::DeclaredIdentifier {
366 self.node.DeclaredIdentifier()
367 }
368
369 fn lookup_interface(
372 &self,
373 tr: &TypeRegister,
374 diag: &mut BuildDiagnostics,
375 ) -> Result<Rc<Component>, ()> {
376 let interface_name = self.interface_name.to_smolstr();
377 match tr.lookup_element(&interface_name) {
378 Ok(element_type) => match element_type {
379 ElementType::Component(component) => {
380 if !component.is_interface() {
381 diag.push_error(
382 format!("'{}' is not an interface", self.interface_name),
383 &self.interface_name_node(),
384 );
385 return Err(());
386 }
387
388 Ok(component)
389 }
390 _ => {
391 diag.push_error(
392 format!("'{}' is not an interface", self.interface_name),
393 &self.interface_name_node(),
394 );
395 Err(())
396 }
397 },
398 Err(error) => {
399 diag.push_error(error, &self.interface_name_node());
400 Err(())
401 }
402 }
403 }
404}
405
406impl From<&syntax_nodes::UsesIdentifier> for UsesStatement {
407 fn from(node: &syntax_nodes::UsesIdentifier) -> UsesStatement {
408 UsesStatement {
409 interface_name: QualifiedTypeName::from_node(
410 node.child_node(SyntaxKind::QualifiedName).unwrap().clone().into(),
411 ),
412 child_id: parser::identifier_text(&node.DeclaredIdentifier()).unwrap_or_default(),
413 node: node.clone(),
414 }
415 }
416}
417
418#[derive(Debug, Default, Clone)]
419pub struct InitCode {
420 pub constructor_code: Vec<Expression>,
422 pub focus_setting_code: Vec<Expression>,
424 pub font_registration_code: Vec<Expression>,
426
427 pub inlined_init_code: BTreeMap<usize, Expression>,
430}
431
432impl InitCode {
433 pub fn iter(&self) -> impl Iterator<Item = &Expression> {
434 self.font_registration_code
435 .iter()
436 .chain(self.focus_setting_code.iter())
437 .chain(self.constructor_code.iter())
438 .chain(self.inlined_init_code.values())
439 }
440 pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut Expression> {
441 self.font_registration_code
442 .iter_mut()
443 .chain(self.focus_setting_code.iter_mut())
444 .chain(self.constructor_code.iter_mut())
445 .chain(self.inlined_init_code.values_mut())
446 }
447}
448
449#[derive(Default, Debug)]
452pub struct Component {
453 pub node: Option<syntax_nodes::Component>,
454 pub id: SmolStr,
455 pub root_element: ElementRc,
456
457 pub parent_element: Weak<RefCell<Element>>,
459
460 pub optimized_elements: RefCell<Vec<ElementRc>>,
463
464 pub root_constraints: RefCell<LayoutConstraints>,
466
467 pub child_insertion_point: RefCell<Option<ChildrenInsertionPoint>>,
470
471 pub init_code: RefCell<InitCode>,
472
473 pub popup_windows: RefCell<Vec<PopupWindow>>,
474 pub timers: RefCell<Vec<Timer>>,
475 pub menu_item_tree: RefCell<Vec<Rc<Component>>>,
476
477 pub inherits_popup_window: Cell<bool>,
479
480 pub exported_global_names: RefCell<Vec<ExportedName>>,
483
484 pub used: Cell<bool>,
486
487 pub private_properties: RefCell<Vec<(SmolStr, Type)>>,
490
491 pub from_library: Cell<bool>,
493}
494
495impl Component {
496 pub fn from_node(
497 node: syntax_nodes::Component,
498 diag: &mut BuildDiagnostics,
499 tr: &TypeRegister,
500 ) -> Rc<Self> {
501 let mut child_insertion_point = None;
502 let is_legacy_syntax = node.child_token(SyntaxKind::ColonEqual).is_some();
503 let c = Component {
504 node: Some(node.clone()),
505 id: parser::identifier_text(&node.DeclaredIdentifier()).unwrap_or_default(),
506 root_element: Element::from_node(
507 node.Element(),
508 "root".into(),
509 match node.child_text(SyntaxKind::Identifier) {
510 Some(t) if t == "global" => ElementType::Global,
511 Some(t) if t == "interface" => {
512 if !diag.enable_experimental {
513 diag.push_error("'interface' is an experimental feature".into(), &node);
514 ElementType::Error
515 } else {
516 ElementType::Interface
517 }
518 }
519 _ => ElementType::Error,
520 },
521 &mut child_insertion_point,
522 is_legacy_syntax,
523 diag,
524 tr,
525 ),
526 child_insertion_point: RefCell::new(child_insertion_point),
527 ..Default::default()
528 };
529 let c = Rc::new(c);
530 let weak = Rc::downgrade(&c);
531 recurse_elem(&c.root_element, &(), &mut |e, _| {
532 e.borrow_mut().enclosing_component = weak.clone();
533 if let Some(qualified_id) =
534 e.borrow_mut().debug.first_mut().and_then(|x| x.qualified_id.as_mut())
535 {
536 *qualified_id = format_smolstr!("{}::{}", c.id, qualified_id);
537 }
538 });
539 apply_uses_statement(&c.root_element, node.UsesSpecifier(), tr, diag);
540 c
541 }
542
543 pub fn is_global(&self) -> bool {
545 match &self.root_element.borrow().base_type {
546 ElementType::Global => true,
547 ElementType::Builtin(c) => c.is_global,
548 _ => false,
549 }
550 }
551
552 pub fn is_interface(&self) -> bool {
554 matches!(&self.root_element.borrow().base_type, ElementType::Interface)
555 }
556
557 pub fn global_aliases(&self) -> Vec<SmolStr> {
560 self.exported_global_names
561 .borrow()
562 .iter()
563 .filter(|name| name.as_str() != self.root_element.borrow().id)
564 .map(|name| name.original_name())
565 .collect()
566 }
567
568 pub fn repeater_count(&self) -> u32 {
570 let mut count = 0;
571 recurse_elem(&self.root_element, &(), &mut |element, _| {
572 let element = element.borrow();
573 if let Some(sub_component) = element.sub_component() {
574 count += sub_component.repeater_count();
575 } else if element.repeated.is_some() {
576 count += 1;
577 }
578 });
579 count
580 }
581}
582
583#[derive(Copy, Clone, Debug, Eq, PartialEq, Default)]
584pub enum PropertyVisibility {
585 #[default]
586 Private,
587 Input,
588 Output,
589 InOut,
590 Constexpr,
592 Fake,
595 Public,
597 Protected,
598}
599
600impl Display for PropertyVisibility {
601 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
602 match self {
603 PropertyVisibility::Private => f.write_str("private"),
604 PropertyVisibility::Input => f.write_str("input"),
605 PropertyVisibility::Output => f.write_str("output"),
606 PropertyVisibility::InOut => f.write_str("input output"),
607 PropertyVisibility::Constexpr => f.write_str("constexpr"),
608 PropertyVisibility::Public => f.write_str("public"),
609 PropertyVisibility::Protected => f.write_str("protected"),
610 PropertyVisibility::Fake => f.write_str("fake"),
611 }
612 }
613}
614
615#[derive(Clone, Debug, Default)]
616pub struct PropertyDeclaration {
617 pub property_type: Type,
618 pub node: Option<SyntaxNode>,
619 pub expose_in_public_api: bool,
621 pub is_alias: Option<NamedReference>,
623 pub visibility: PropertyVisibility,
624 pub pure: Option<bool>,
626}
627
628impl PropertyDeclaration {
629 pub fn type_node(&self) -> Option<SyntaxNode> {
631 let node = self.node.as_ref()?;
632 if let Some(x) = syntax_nodes::PropertyDeclaration::new(node.clone()) {
633 Some(x.Type().map_or_else(|| x.into(), |x| x.into()))
634 } else {
635 node.clone().into()
636 }
637 }
638}
639
640impl From<Type> for PropertyDeclaration {
641 fn from(ty: Type) -> Self {
642 PropertyDeclaration { property_type: ty, ..Self::default() }
643 }
644}
645
646#[derive(Debug, Clone, Copy, PartialEq)]
647pub enum TransitionDirection {
648 In,
649 Out,
650 InOut,
651}
652
653#[derive(Debug, Clone)]
654pub struct TransitionPropertyAnimation {
655 pub state_id: i32,
657 pub direction: TransitionDirection,
659 pub animation: ElementRc,
661}
662
663impl TransitionPropertyAnimation {
664 pub fn condition(&self, state: Expression) -> Expression {
667 match self.direction {
668 TransitionDirection::In => Expression::BinaryExpression {
669 lhs: Box::new(Expression::StructFieldAccess {
670 base: Box::new(state),
671 name: "current-state".into(),
672 }),
673 rhs: Box::new(Expression::NumberLiteral(self.state_id as _, Unit::None)),
674 op: '=',
675 },
676 TransitionDirection::Out => Expression::BinaryExpression {
677 lhs: Box::new(Expression::StructFieldAccess {
678 base: Box::new(state),
679 name: "previous-state".into(),
680 }),
681 rhs: Box::new(Expression::NumberLiteral(self.state_id as _, Unit::None)),
682 op: '=',
683 },
684 TransitionDirection::InOut => Expression::BinaryExpression {
685 lhs: Box::new(Expression::BinaryExpression {
686 lhs: Box::new(Expression::StructFieldAccess {
687 base: Box::new(state.clone()),
688 name: "current-state".into(),
689 }),
690 rhs: Box::new(Expression::NumberLiteral(self.state_id as _, Unit::None)),
691 op: '=',
692 }),
693 rhs: Box::new(Expression::BinaryExpression {
694 lhs: Box::new(Expression::StructFieldAccess {
695 base: Box::new(state),
696 name: "previous-state".into(),
697 }),
698 rhs: Box::new(Expression::NumberLiteral(self.state_id as _, Unit::None)),
699 op: '=',
700 }),
701 op: '|',
702 },
703 }
704 }
705}
706
707#[derive(Debug)]
708pub enum PropertyAnimation {
709 Static(ElementRc),
710 Transition { state_ref: Expression, animations: Vec<TransitionPropertyAnimation> },
711}
712
713impl Clone for PropertyAnimation {
714 fn clone(&self) -> Self {
715 fn deep_clone(e: &ElementRc) -> ElementRc {
716 let e = e.borrow();
717 debug_assert!(e.children.is_empty());
718 debug_assert!(e.property_declarations.is_empty());
719 debug_assert!(e.states.is_empty() && e.transitions.is_empty());
720 Rc::new(RefCell::new(Element {
721 id: e.id.clone(),
722 base_type: e.base_type.clone(),
723 bindings: e.bindings.clone(),
724 property_analysis: e.property_analysis.clone(),
725 enclosing_component: e.enclosing_component.clone(),
726 repeated: None,
727 debug: e.debug.clone(),
728 ..Default::default()
729 }))
730 }
731 match self {
732 PropertyAnimation::Static(e) => PropertyAnimation::Static(deep_clone(e)),
733 PropertyAnimation::Transition { state_ref, animations } => {
734 PropertyAnimation::Transition {
735 state_ref: state_ref.clone(),
736 animations: animations
737 .iter()
738 .map(|t| TransitionPropertyAnimation {
739 state_id: t.state_id,
740 direction: t.direction,
741 animation: deep_clone(&t.animation),
742 })
743 .collect(),
744 }
745 }
746 }
747 }
748}
749
750#[derive(Default, Clone)]
752pub struct AccessibilityProps(pub BTreeMap<String, NamedReference>);
753
754#[derive(Clone, Debug)]
755pub struct GeometryProps {
756 pub x: NamedReference,
757 pub y: NamedReference,
758 pub width: NamedReference,
759 pub height: NamedReference,
760}
761
762impl GeometryProps {
763 pub fn new(element: &ElementRc) -> Self {
764 Self {
765 x: NamedReference::new(element, SmolStr::new_static("x")),
766 y: NamedReference::new(element, SmolStr::new_static("y")),
767 width: NamedReference::new(element, SmolStr::new_static("width")),
768 height: NamedReference::new(element, SmolStr::new_static("height")),
769 }
770 }
771}
772
773pub type BindingsMap = BTreeMap<SmolStr, RefCell<BindingExpression>>;
774
775#[derive(Clone, Debug)]
776pub struct ElementDebugInfo {
777 pub qualified_id: Option<SmolStr>,
779 pub type_name: String,
780 pub element_hash: u64,
785 pub node: syntax_nodes::Element,
786 pub layout: Option<crate::layout::Layout>,
789 pub element_boundary: bool,
793}
794
795impl ElementDebugInfo {
796 fn encoded_element_info(&self) -> String {
799 let mut info = self.type_name.clone();
800 info.push(',');
801 if let Some(id) = self.qualified_id.as_ref() {
802 info.push_str(id);
803 }
804 info
805 }
806}
807
808#[derive(Default)]
810pub struct Element {
811 pub id: SmolStr,
817 pub base_type: ElementType,
819 pub bindings: BindingsMap,
821 pub change_callbacks: BTreeMap<SmolStr, RefCell<Vec<Expression>>>,
822 pub property_analysis: RefCell<HashMap<SmolStr, PropertyAnalysis>>,
823
824 pub children: Vec<ElementRc>,
825 pub enclosing_component: Weak<Component>,
827
828 pub property_declarations: BTreeMap<SmolStr, PropertyDeclaration>,
829
830 pub named_references: crate::namedreference::NamedReferenceContainer,
832
833 pub repeated: Option<RepeatedElementInfo>,
835 pub is_component_placeholder: bool,
837
838 pub states: Vec<State>,
839 pub transitions: Vec<Transition>,
840
841 pub child_of_layout: bool,
843 pub layout_info_prop: Option<(NamedReference, NamedReference)>,
845 pub default_fill_parent: (bool, bool),
847
848 pub accessibility_props: AccessibilityProps,
849
850 pub geometry_props: Option<GeometryProps>,
853
854 pub is_flickable_viewport: bool,
856
857 pub has_popup_child: bool,
860
861 pub item_index: OnceCell<u32>,
864 pub item_index_of_first_children: OnceCell<u32>,
866
867 pub is_legacy_syntax: bool,
869
870 pub inline_depth: i32,
872
873 pub grid_layout_cell: Option<Rc<RefCell<crate::layout::GridLayoutCell>>>,
875
876 pub debug: Vec<ElementDebugInfo>,
882}
883
884impl Spanned for Element {
885 fn span(&self) -> crate::diagnostics::Span {
886 self.debug
887 .first()
888 .map(|n| {
889 n.node.QualifiedName().as_ref().map(Spanned::span).unwrap_or_else(|| n.node.span())
894 })
895 .unwrap_or_default()
896 }
897
898 fn source_file(&self) -> Option<&crate::diagnostics::SourceFile> {
899 self.debug.first().map(|n| &n.node.source_file)
900 }
901}
902
903impl core::fmt::Debug for Element {
904 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
905 pretty_print(f, self, 0)
906 }
907}
908
909pub fn pretty_print(
910 f: &mut impl std::fmt::Write,
911 e: &Element,
912 indentation: usize,
913) -> std::fmt::Result {
914 if let Some(repeated) = &e.repeated {
915 write!(f, "for {}[{}] in ", repeated.model_data_id, repeated.index_id)?;
916 expression_tree::pretty_print(f, &repeated.model)?;
917 write!(f, ":")?;
918 if let ElementType::Component(base) = &e.base_type {
919 write!(f, "(base) ")?;
920 if base.parent_element.upgrade().is_some() {
921 pretty_print(f, &base.root_element.borrow(), indentation)?;
922 return Ok(());
923 }
924 }
925 }
926 if e.is_component_placeholder {
927 write!(f, "/* Component Placeholder */ ")?;
928 }
929 writeln!(f, "{} := {} {{ /* {} */", e.id, e.base_type, e.element_infos())?;
930 let mut indentation = indentation + 1;
931 macro_rules! indent {
932 () => {
933 for _ in 0..indentation {
934 write!(f, " ")?
935 }
936 };
937 }
938 for (name, ty) in &e.property_declarations {
939 indent!();
940 if let Some(alias) = &ty.is_alias {
941 writeln!(f, "alias<{}> {} <=> {:?};", ty.property_type, name, alias)?
942 } else {
943 writeln!(f, "property<{}> {};", ty.property_type, name)?
944 }
945 }
946 for (name, expr) in &e.bindings {
947 indent!();
948 write!(f, "{name}: ")?;
949 let Ok(expr) = expr.try_borrow() else {
950 writeln!(f, "<borrowed>")?;
951 continue;
952 };
953 expression_tree::pretty_print(f, &expr.expression)?;
954 if expr.analysis.as_ref().is_some_and(|a| a.is_const) {
955 write!(f, "/*const*/")?;
956 }
957 writeln!(f, ";")?;
958 if let Some(anim) = &expr.animation {
960 indent!();
961 writeln!(f, "animate {name} {anim:?}")?;
962 }
963 for nr in &expr.two_way_bindings {
964 indent!();
965 writeln!(f, "{name} <=> {nr:?};")?;
966 }
967 }
968 for (name, ch) in &e.change_callbacks {
969 for ex in &*ch.borrow() {
970 indent!();
971 write!(f, "changed {name} => ")?;
972 expression_tree::pretty_print(f, ex)?;
973 writeln!(f)?;
974 }
975 }
976 if !e.states.is_empty() {
977 indent!();
978 writeln!(f, "states {:?}", e.states)?;
979 }
980 if !e.transitions.is_empty() {
981 indent!();
982 writeln!(f, "transitions {:?} ", e.transitions)?;
983 }
984 for c in &e.children {
985 indent!();
986 pretty_print(f, &c.borrow(), indentation)?
987 }
988 if let Some(g) = &e.geometry_props {
989 indent!();
990 writeln!(f, "geometry {g:?} ")?;
991 }
992
993 indentation -= 1;
997 indent!();
998 writeln!(f, "}}")
999}
1000
1001#[derive(Clone, Default, Debug)]
1002pub struct PropertyAnalysis {
1003 pub is_set: bool,
1005
1006 pub is_set_externally: bool,
1008
1009 pub is_read: bool,
1012
1013 pub is_read_externally: bool,
1015
1016 pub is_linked_to_read_only: bool,
1018
1019 pub is_linked: bool,
1021}
1022
1023impl PropertyAnalysis {
1024 pub fn merge_with_base(&mut self, other: &PropertyAnalysis) {
1029 self.is_set |= other.is_set;
1030 self.is_read |= other.is_read;
1031 }
1032
1033 pub fn merge(&mut self, other: &PropertyAnalysis) {
1035 self.is_set |= other.is_set;
1036 self.is_read |= other.is_read;
1037 self.is_read_externally |= other.is_read_externally;
1038 self.is_set_externally |= other.is_set_externally;
1039 }
1040
1041 pub fn is_used(&self) -> bool {
1043 self.is_read || self.is_read_externally || self.is_set || self.is_set_externally
1044 }
1045}
1046
1047#[derive(Debug, Clone)]
1048pub struct ListViewInfo {
1049 pub viewport_y: NamedReference,
1050 pub viewport_height: NamedReference,
1051 pub viewport_width: NamedReference,
1052 pub listview_height: NamedReference,
1054 pub listview_width: NamedReference,
1056}
1057
1058#[derive(Debug, Clone)]
1059pub struct RepeatedElementInfo {
1061 pub model: Expression,
1062 pub model_data_id: SmolStr,
1063 pub index_id: SmolStr,
1064 pub is_conditional_element: bool,
1068 pub is_listview: Option<ListViewInfo>,
1070}
1071
1072pub type ElementRc = Rc<RefCell<Element>>;
1073pub type ElementWeak = Weak<RefCell<Element>>;
1074
1075#[derive(Debug, PartialEq)]
1076enum ParentRelationship {
1078 Inherits,
1080 Implements,
1082}
1083
1084fn expected_relationship_to_parent(node: &syntax_nodes::Element) -> Option<ParentRelationship> {
1086 let parent = node.parent().filter(|p| p.kind() == SyntaxKind::Component)?;
1087 let implements_inherits_identifier =
1088 parent.children_with_tokens().filter(|n| n.kind() == SyntaxKind::Identifier).nth(1)?;
1089 let token = implements_inherits_identifier.as_token()?;
1090 match token.text() {
1091 "inherits" => Some(ParentRelationship::Inherits),
1092 "implements" => Some(ParentRelationship::Implements),
1093 _ => None,
1094 }
1095}
1096
1097impl Element {
1098 pub fn make_rc(self) -> ElementRc {
1099 let r = ElementRc::new(RefCell::new(self));
1100 let g = GeometryProps::new(&r);
1101 r.borrow_mut().geometry_props = Some(g);
1102 r
1103 }
1104
1105 pub fn from_node(
1106 node: syntax_nodes::Element,
1107 id: SmolStr,
1108 parent_type: ElementType,
1109 component_child_insertion_point: &mut Option<ChildrenInsertionPoint>,
1110 is_legacy_syntax: bool,
1111 diag: &mut BuildDiagnostics,
1112 tr: &TypeRegister,
1113 ) -> ElementRc {
1114 let mut interfaces: Vec<Rc<Component>> = Vec::new();
1115 let base_type = if let Some(base_node) = node.QualifiedName() {
1116 let base = QualifiedTypeName::from_node(base_node.clone());
1117 let base_string = base.to_smolstr();
1118 match (
1119 parent_type.lookup_type_for_child_element(&base_string, tr),
1120 expected_relationship_to_parent(&node),
1121 ) {
1122 (Ok(ElementType::Component(c)), _) if c.is_global() => {
1123 diag.push_error(
1124 "Cannot create an instance of a global component".into(),
1125 &base_node,
1126 );
1127 ElementType::Error
1128 }
1129 (Ok(ElementType::Component(c)), Some(ParentRelationship::Implements)) => {
1130 if !diag.enable_experimental {
1131 diag.push_error(
1132 "'implements' is an experimental feature".into(),
1133 &base_node,
1134 );
1135 ElementType::Error
1136 } else if !c.is_interface() {
1137 diag.push_error(
1138 format!("Cannot implement {}. It is not an interface", base_string),
1139 &base_node,
1140 );
1141 ElementType::Error
1142 } else {
1143 c.used.set(true);
1144 interfaces.push(c);
1145 tr.empty_type()
1147 }
1148 }
1149 (Ok(ElementType::Builtin(_bt)), Some(ParentRelationship::Implements)) => {
1150 if !diag.enable_experimental {
1151 diag.push_error(
1152 "'implements' is an experimental feature".into(),
1153 &base_node,
1154 );
1155 } else {
1156 diag.push_error(
1157 format!("Cannot implement {}. It is not an interface", base_string),
1158 &base_node,
1159 );
1160 }
1161 ElementType::Error
1162 }
1163 (Ok(ty), _) => ty,
1164 (Err(err), _) => {
1165 diag.push_error(err, &base_node);
1166 ElementType::Error
1167 }
1168 }
1169 } else if parent_type == ElementType::Global || parent_type == ElementType::Interface {
1170 let mut error_on = |node: &dyn Spanned, what: &str| {
1172 let element_type = match parent_type {
1173 ElementType::Global => "A global component",
1174 ElementType::Interface => "An interface",
1175 _ => "An unexpected type",
1176 };
1177 diag.push_error(format!("{element_type} cannot have {what}"), node);
1178 };
1179 node.SubElement().for_each(|n| error_on(&n, "sub elements"));
1180 node.RepeatedElement().for_each(|n| error_on(&n, "sub elements"));
1181 if let Some(n) = node.ChildrenPlaceholder() {
1182 error_on(&n, "sub elements");
1183 }
1184 node.PropertyAnimation().for_each(|n| error_on(&n, "animations"));
1185 node.States().for_each(|n| error_on(&n, "states"));
1186 node.Transitions().for_each(|n| error_on(&n, "transitions"));
1187 node.CallbackDeclaration().for_each(|cb| {
1188 if parser::identifier_text(&cb.DeclaredIdentifier()).is_some_and(|s| s == "init") {
1189 error_on(&cb, "an 'init' callback")
1190 }
1191 });
1192 node.CallbackConnection().for_each(|cb| {
1193 if parser::identifier_text(&cb).is_some_and(|s| s == "init") {
1194 error_on(&cb, "an 'init' callback")
1195 }
1196 });
1197
1198 parent_type
1199 } else if parent_type != ElementType::Error {
1200 assert!(diag.has_errors());
1202 return ElementRc::default();
1203 } else {
1204 tr.empty_type()
1205 };
1206 let qualified_id = (!id.is_empty()).then(|| id.clone());
1208 if let ElementType::Component(c) = &base_type {
1209 c.used.set(true);
1210 }
1211 let type_name = base_type
1212 .type_name()
1213 .filter(|_| base_type != tr.empty_type())
1214 .unwrap_or_default()
1215 .to_string();
1216 let mut r = Element {
1217 id,
1218 base_type: base_type.clone(),
1219 debug: vec![ElementDebugInfo {
1220 qualified_id,
1221 element_hash: 0,
1222 type_name,
1223 node: node.clone(),
1224 layout: None,
1225 element_boundary: false,
1226 }],
1227 is_legacy_syntax,
1228 ..Default::default()
1229 };
1230
1231 for interface in interfaces.iter() {
1232 for (prop_name, prop_decl) in
1233 interface.root_element.borrow().property_declarations.iter()
1234 {
1235 r.property_declarations.insert(prop_name.clone(), prop_decl.clone());
1236 }
1237 }
1238
1239 for prop_decl in node.PropertyDeclaration() {
1240 let prop_type = prop_decl
1241 .Type()
1242 .map(|type_node| type_from_node(type_node, diag, tr))
1243 .unwrap_or(Type::InferredProperty);
1245
1246 let unresolved_prop_name =
1247 unwrap_or_continue!(parser::identifier_text(&prop_decl.DeclaredIdentifier()); diag);
1248 let PropertyLookupResult {
1249 resolved_name: prop_name,
1250 property_type: maybe_existing_prop_type,
1251 ..
1252 } = r.lookup_property(&unresolved_prop_name);
1253 match maybe_existing_prop_type {
1254 Type::Callback { .. } => {
1255 diag.push_error(
1256 format!("Cannot declare property '{prop_name}' when a callback with the same name exists"),
1257 &prop_decl.DeclaredIdentifier().child_token(SyntaxKind::Identifier).unwrap(),
1258 );
1259 continue;
1260 }
1261 Type::Function { .. } => {
1262 diag.push_error(
1263 format!("Cannot declare property '{prop_name}' when a function with the same name exists"),
1264 &prop_decl.DeclaredIdentifier().child_token(SyntaxKind::Identifier).unwrap(),
1265 );
1266 continue;
1267 }
1268 Type::Invalid => {} _ => {
1270 diag.push_error(
1271 format!("Cannot override property '{unresolved_prop_name}'"),
1272 &prop_decl
1273 .DeclaredIdentifier()
1274 .child_token(SyntaxKind::Identifier)
1275 .unwrap(),
1276 );
1277 continue;
1278 }
1279 }
1280
1281 let mut visibility = None;
1282 for token in prop_decl.children_with_tokens() {
1283 if token.kind() != SyntaxKind::Identifier {
1284 continue;
1285 }
1286 match (token.as_token().unwrap().text(), visibility) {
1287 ("in", None) => visibility = Some(PropertyVisibility::Input),
1288 ("in", Some(_)) => diag.push_error("Extra 'in' keyword".into(), &token),
1289 ("out", None) => visibility = Some(PropertyVisibility::Output),
1290 ("out", Some(_)) => diag.push_error("Extra 'out' keyword".into(), &token),
1291 ("in-out" | "in_out", None) => visibility = Some(PropertyVisibility::InOut),
1292 ("in-out" | "in_out", Some(_)) => {
1293 diag.push_error("Extra 'in-out' keyword".into(), &token)
1294 }
1295 ("private", None) => visibility = Some(PropertyVisibility::Private),
1296 ("private", Some(_)) => {
1297 diag.push_error("Extra 'private' keyword".into(), &token)
1298 }
1299 _ => (),
1300 }
1301 }
1302 let visibility = visibility.unwrap_or({
1303 if is_legacy_syntax {
1304 PropertyVisibility::InOut
1305 } else {
1306 PropertyVisibility::Private
1307 }
1308 });
1309
1310 if base_type == ElementType::Interface && visibility == PropertyVisibility::Private {
1311 diag.push_error(
1312 "'private' properties are inaccessible in an interface".into(),
1313 &prop_decl,
1314 );
1315 }
1316
1317 r.property_declarations.insert(
1318 prop_name.clone().into(),
1319 PropertyDeclaration {
1320 property_type: prop_type,
1321 node: Some(prop_decl.clone().into()),
1322 visibility,
1323 ..Default::default()
1324 },
1325 );
1326
1327 if let Some(csn) = prop_decl.BindingExpression() {
1328 match r.bindings.entry(prop_name.clone().into()) {
1329 Entry::Vacant(e) => {
1330 e.insert(BindingExpression::new_uncompiled(csn.into()).into());
1331 }
1332 Entry::Occupied(_) => {
1333 diag.push_error(
1334 "Duplicated property binding".into(),
1335 &prop_decl.DeclaredIdentifier(),
1336 );
1337 }
1338 }
1339 }
1340 if let Some(csn) = prop_decl.TwoWayBinding()
1341 && r.bindings
1342 .insert(prop_name.into(), BindingExpression::new_uncompiled(csn.into()).into())
1343 .is_some()
1344 {
1345 diag.push_error(
1346 "Duplicated property binding".into(),
1347 &prop_decl.DeclaredIdentifier(),
1348 );
1349 }
1350 }
1351
1352 r.parse_bindings(
1353 node.Binding().filter_map(|b| {
1354 Some((b.child_token(SyntaxKind::Identifier)?, b.BindingExpression().into()))
1355 }),
1356 is_legacy_syntax,
1357 diag,
1358 );
1359 r.parse_bindings(
1360 node.TwoWayBinding()
1361 .filter_map(|b| Some((b.child_token(SyntaxKind::Identifier)?, b.into()))),
1362 is_legacy_syntax,
1363 diag,
1364 );
1365
1366 apply_default_type_properties(&mut r);
1367
1368 for sig_decl in node.CallbackDeclaration() {
1369 let name =
1370 unwrap_or_continue!(parser::identifier_text(&sig_decl.DeclaredIdentifier()); diag);
1371
1372 let pure = Some(
1373 sig_decl.child_token(SyntaxKind::Identifier).is_some_and(|t| t.text() == "pure"),
1374 );
1375
1376 let PropertyLookupResult {
1377 resolved_name: existing_name,
1378 property_type: maybe_existing_prop_type,
1379 ..
1380 } = r.lookup_property(&name);
1381 if !matches!(maybe_existing_prop_type, Type::Invalid) {
1382 if matches!(maybe_existing_prop_type, Type::Callback { .. }) {
1383 if r.property_declarations.contains_key(&name) {
1384 diag.push_error(
1385 "Duplicated callback declaration".into(),
1386 &sig_decl.DeclaredIdentifier(),
1387 );
1388 } else {
1389 diag.push_error(
1390 format!("Cannot override callback '{existing_name}'"),
1391 &sig_decl.DeclaredIdentifier(),
1392 )
1393 }
1394 } else {
1395 diag.push_error(
1396 format!(
1397 "Cannot declare callback '{existing_name}' when a {} with the same name exists",
1398 if matches!(maybe_existing_prop_type, Type::Function { .. }) { "function" } else { "property" }
1399 ),
1400 &sig_decl.DeclaredIdentifier(),
1401 );
1402 }
1403 continue;
1404 }
1405
1406 if let Some(csn) = sig_decl.TwoWayBinding() {
1407 r.bindings
1408 .insert(name.clone(), BindingExpression::new_uncompiled(csn.into()).into());
1409 r.property_declarations.insert(
1410 name,
1411 PropertyDeclaration {
1412 property_type: Type::InferredCallback,
1413 node: Some(sig_decl.into()),
1414 visibility: PropertyVisibility::InOut,
1415 pure,
1416 ..Default::default()
1417 },
1418 );
1419 continue;
1420 }
1421
1422 let args = sig_decl
1423 .CallbackDeclarationParameter()
1424 .map(|p| type_from_node(p.Type(), diag, tr))
1425 .collect();
1426 let return_type = sig_decl
1427 .ReturnType()
1428 .map(|ret_ty| type_from_node(ret_ty.Type(), diag, tr))
1429 .unwrap_or(Type::Void);
1430 let arg_names = sig_decl
1431 .CallbackDeclarationParameter()
1432 .map(|a| {
1433 a.DeclaredIdentifier()
1434 .and_then(|x| parser::identifier_text(&x))
1435 .unwrap_or_default()
1436 })
1437 .collect();
1438 r.property_declarations.insert(
1439 name,
1440 PropertyDeclaration {
1441 property_type: Type::Callback(Rc::new(Function {
1442 return_type,
1443 args,
1444 arg_names,
1445 })),
1446 node: Some(sig_decl.into()),
1447 visibility: PropertyVisibility::InOut,
1448 pure,
1449 ..Default::default()
1450 },
1451 );
1452 }
1453
1454 for func in node.Function() {
1455 let name =
1456 unwrap_or_continue!(parser::identifier_text(&func.DeclaredIdentifier()); diag);
1457
1458 let PropertyLookupResult {
1459 resolved_name: existing_name,
1460 property_type: maybe_existing_prop_type,
1461 ..
1462 } = r.lookup_property(&name);
1463 if !matches!(maybe_existing_prop_type, Type::Invalid) {
1464 if matches!(maybe_existing_prop_type, Type::Callback { .. } | Type::Function { .. })
1465 {
1466 diag.push_error(
1467 format!("Cannot override '{existing_name}'"),
1468 &func.DeclaredIdentifier(),
1469 )
1470 } else {
1471 diag.push_error(
1472 format!("Cannot declare function '{existing_name}' when a property with the same name exists"),
1473 &func.DeclaredIdentifier(),
1474 );
1475 }
1476 continue;
1477 }
1478
1479 let mut args = Vec::new();
1480 let mut arg_names = Vec::new();
1481 for a in func.ArgumentDeclaration() {
1482 args.push(type_from_node(a.Type(), diag, tr));
1483 let name =
1484 unwrap_or_continue!(parser::identifier_text(&a.DeclaredIdentifier()); diag);
1485 if arg_names.contains(&name) {
1486 diag.push_error(
1487 format!("Duplicated argument name '{name}'"),
1488 &a.DeclaredIdentifier(),
1489 );
1490 }
1491 arg_names.push(name);
1492 }
1493 let return_type = func
1494 .ReturnType()
1495 .map_or(Type::Void, |ret_ty| type_from_node(ret_ty.Type(), diag, tr));
1496 if r.bindings
1497 .insert(name.clone(), BindingExpression::new_uncompiled(func.clone().into()).into())
1498 .is_some()
1499 {
1500 assert!(diag.has_errors());
1501 }
1502
1503 let mut visibility = PropertyVisibility::Private;
1504 let mut pure = None;
1505 for token in func.children_with_tokens() {
1506 if token.kind() != SyntaxKind::Identifier {
1507 continue;
1508 }
1509 match token.as_token().unwrap().text() {
1510 "pure" => pure = Some(true),
1511 "public" => {
1512 visibility = PropertyVisibility::Public;
1513 pure = pure.or(Some(false));
1514 }
1515 "protected" => {
1516 visibility = PropertyVisibility::Protected;
1517 pure = pure.or(Some(false));
1518 }
1519 _ => (),
1520 }
1521 }
1522
1523 r.property_declarations.insert(
1524 name,
1525 PropertyDeclaration {
1526 property_type: Type::Function(Rc::new(Function {
1527 return_type,
1528 args,
1529 arg_names,
1530 })),
1531 node: Some(func.into()),
1532 visibility,
1533 pure,
1534 ..Default::default()
1535 },
1536 );
1537 }
1538
1539 for con_node in node.CallbackConnection() {
1540 let unresolved_name = unwrap_or_continue!(parser::identifier_text(&con_node); diag);
1541 let PropertyLookupResult { resolved_name, property_type, .. } =
1542 r.lookup_property(&unresolved_name);
1543 if let Type::Callback(callback) = &property_type {
1544 let num_arg = con_node.DeclaredIdentifier().count();
1545 if num_arg > callback.args.len() {
1546 diag.push_error(
1547 format!(
1548 "'{}' only has {} arguments, but {} were provided",
1549 unresolved_name,
1550 callback.args.len(),
1551 num_arg
1552 ),
1553 &con_node.child_token(SyntaxKind::Identifier).unwrap(),
1554 );
1555 }
1556 } else if property_type == Type::InferredCallback {
1557 } else {
1559 if r.base_type != ElementType::Error {
1560 diag.push_error(
1561 format!("'{}' is not a callback in {}", unresolved_name, r.base_type),
1562 &con_node.child_token(SyntaxKind::Identifier).unwrap(),
1563 );
1564 }
1565 continue;
1566 }
1567 match r.bindings.entry(resolved_name.into()) {
1568 Entry::Vacant(e) => {
1569 e.insert(BindingExpression::new_uncompiled(con_node.clone().into()).into());
1570 }
1571 Entry::Occupied(_) => diag.push_error(
1572 "Duplicated callback".into(),
1573 &con_node.child_token(SyntaxKind::Identifier).unwrap(),
1574 ),
1575 }
1576 }
1577
1578 for anim in node.PropertyAnimation() {
1579 if let Some(star) = anim.child_token(SyntaxKind::Star) {
1580 diag.push_error(
1581 "catch-all property is only allowed within transitions".into(),
1582 &star,
1583 )
1584 };
1585 for prop_name_token in anim.QualifiedName() {
1586 match QualifiedTypeName::from_node(prop_name_token.clone()).members.as_slice() {
1587 [unresolved_prop_name] => {
1588 if r.base_type == ElementType::Error {
1589 continue;
1590 };
1591 let lookup_result = r.lookup_property(unresolved_prop_name);
1592 let valid_assign = lookup_result.is_valid_for_assignment();
1593 if let Some(anim_element) = animation_element_from_node(
1594 &anim,
1595 &prop_name_token,
1596 lookup_result.property_type,
1597 diag,
1598 tr,
1599 ) {
1600 if !valid_assign {
1601 diag.push_error(
1602 format!(
1603 "Cannot animate {} property '{}'",
1604 lookup_result.property_visibility, unresolved_prop_name
1605 ),
1606 &prop_name_token,
1607 );
1608 }
1609
1610 if unresolved_prop_name != lookup_result.resolved_name.as_ref() {
1611 diag.push_property_deprecation_warning(
1612 unresolved_prop_name,
1613 &lookup_result.resolved_name,
1614 &prop_name_token,
1615 );
1616 }
1617
1618 let expr_binding = r
1619 .bindings
1620 .entry(lookup_result.resolved_name.into())
1621 .or_insert_with(|| {
1622 let mut r = BindingExpression::from(Expression::Invalid);
1623 r.priority = 1;
1624 r.span = Some(prop_name_token.to_source_location());
1625 r.into()
1626 });
1627 if expr_binding
1628 .get_mut()
1629 .animation
1630 .replace(PropertyAnimation::Static(anim_element))
1631 .is_some()
1632 {
1633 diag.push_error("Duplicated animation".into(), &prop_name_token)
1634 }
1635 }
1636 }
1637 _ => diag.push_error(
1638 "Can only refer to property in the current element".into(),
1639 &prop_name_token,
1640 ),
1641 }
1642 }
1643 }
1644
1645 for ch in node.PropertyChangedCallback() {
1646 let Some(prop) = parser::identifier_text(&ch.DeclaredIdentifier()) else { continue };
1647 let lookup_result = r.lookup_property(&prop);
1648 if !lookup_result.is_valid() {
1649 if r.base_type != ElementType::Error {
1650 diag.push_error(
1651 format!("Property '{prop}' does not exist"),
1652 &ch.DeclaredIdentifier(),
1653 );
1654 }
1655 } else if !lookup_result.property_type.is_property_type() {
1656 let what = match lookup_result.property_type {
1657 Type::Function { .. } => "a function",
1658 Type::Callback { .. } => "a callback",
1659 _ => "not a property",
1660 };
1661 diag.push_error(
1662 format!(
1663 "Change callback can only be set on properties, and '{prop}' is {what}"
1664 ),
1665 &ch.DeclaredIdentifier(),
1666 );
1667 } else if lookup_result.property_visibility == PropertyVisibility::Private
1668 && !lookup_result.is_local_to_component
1669 {
1670 diag.push_error(
1671 format!("Change callback on a private property '{prop}'"),
1672 &ch.DeclaredIdentifier(),
1673 );
1674 }
1675 let handler = Expression::Uncompiled(ch.clone().into());
1676 match r.change_callbacks.entry(prop) {
1677 Entry::Vacant(e) => {
1678 e.insert(vec![handler].into());
1679 }
1680 Entry::Occupied(mut e) => {
1681 diag.push_error(
1682 format!("Duplicated change callback on '{}'", e.key()),
1683 &ch.DeclaredIdentifier(),
1684 );
1685 e.get_mut().get_mut().push(handler);
1686 }
1687 }
1688 }
1689
1690 let mut children_placeholder = None;
1691 let r = r.make_rc();
1692
1693 for se in node.children() {
1694 if se.kind() == SyntaxKind::SubElement {
1695 let parent_type = r.borrow().base_type.clone();
1696 r.borrow_mut().children.push(Element::from_sub_element_node(
1697 se.into(),
1698 parent_type,
1699 component_child_insertion_point,
1700 is_legacy_syntax,
1701 diag,
1702 tr,
1703 ));
1704 } else if se.kind() == SyntaxKind::RepeatedElement {
1705 let mut sub_child_insertion_point = None;
1706 let rep = Element::from_repeated_node(
1707 se.into(),
1708 &r,
1709 &mut sub_child_insertion_point,
1710 is_legacy_syntax,
1711 diag,
1712 tr,
1713 );
1714 if let Some(ChildrenInsertionPoint { node: se, .. }) = sub_child_insertion_point {
1715 diag.push_error(
1716 "The @children placeholder cannot appear in a repeated element".into(),
1717 &se,
1718 )
1719 }
1720 r.borrow_mut().children.push(rep);
1721 } else if se.kind() == SyntaxKind::ConditionalElement {
1722 let mut sub_child_insertion_point = None;
1723 let rep = Element::from_conditional_node(
1724 se.into(),
1725 r.borrow().base_type.clone(),
1726 &mut sub_child_insertion_point,
1727 is_legacy_syntax,
1728 diag,
1729 tr,
1730 );
1731 if let Some(ChildrenInsertionPoint { node: se, .. }) = sub_child_insertion_point {
1732 diag.push_error(
1733 "The @children placeholder cannot appear in a conditional element".into(),
1734 &se,
1735 )
1736 }
1737 r.borrow_mut().children.push(rep);
1738 } else if se.kind() == SyntaxKind::ChildrenPlaceholder {
1739 if children_placeholder.is_some() {
1740 diag.push_error(
1741 "The @children placeholder can only appear once in an element".into(),
1742 &se,
1743 )
1744 } else {
1745 children_placeholder = Some((se.clone().into(), r.borrow().children.len()));
1746 }
1747 }
1748 }
1749
1750 if let Some((children_placeholder, index)) = children_placeholder {
1751 if component_child_insertion_point.is_some() {
1752 diag.push_error(
1753 "The @children placeholder can only appear once in an element hierarchy".into(),
1754 &children_placeholder,
1755 )
1756 } else {
1757 *component_child_insertion_point = Some(ChildrenInsertionPoint {
1758 parent: r.clone(),
1759 insertion_index: index,
1760 node: children_placeholder,
1761 });
1762 }
1763 }
1764
1765 for state in node.States().flat_map(|s| s.State()) {
1766 let s = State {
1767 id: parser::identifier_text(&state.DeclaredIdentifier()).unwrap_or_default(),
1768 condition: state.Expression().map(|e| Expression::Uncompiled(e.into())),
1769 property_changes: state
1770 .StatePropertyChange()
1771 .filter_map(|s| {
1772 lookup_property_from_qualified_name_for_state(s.QualifiedName(), &r, diag)
1773 .map(|(ne, ty)| {
1774 if !ty.is_property_type() && !matches!(ty, Type::Invalid) {
1775 diag.push_error(
1776 format!("'{}' is not a property", **s.QualifiedName()),
1777 &s,
1778 );
1779 }
1780 (ne, Expression::Uncompiled(s.BindingExpression().into()), s)
1781 })
1782 })
1783 .collect(),
1784 };
1785 for trs in state.Transition() {
1786 let mut t = Transition::from_node(trs, &r, tr, diag);
1787 t.state_id.clone_from(&s.id);
1788 r.borrow_mut().transitions.push(t);
1789 }
1790 r.borrow_mut().states.push(s);
1791 }
1792
1793 for ts in node.Transitions() {
1794 if !is_legacy_syntax {
1795 diag.push_error("'transitions' block are no longer supported. Use 'in {...}' and 'out {...}' directly in the state definition".into(), &ts);
1796 }
1797 for trs in ts.Transition() {
1798 let trans = Transition::from_node(trs, &r, tr, diag);
1799 r.borrow_mut().transitions.push(trans);
1800 }
1801 }
1802
1803 if r.borrow().base_type.to_smolstr() == "ListView" {
1804 let mut seen_for = false;
1805 for se in node.children() {
1806 if se.kind() == SyntaxKind::RepeatedElement && !seen_for {
1807 seen_for = true;
1808 } else if matches!(
1809 se.kind(),
1810 SyntaxKind::SubElement
1811 | SyntaxKind::ConditionalElement
1812 | SyntaxKind::RepeatedElement
1813 | SyntaxKind::ChildrenPlaceholder
1814 ) {
1815 diag.push_error("A ListView can just have a single 'for' as children. Anything else is not supported".into(), &se)
1816 }
1817 }
1818 }
1819
1820 r
1821 }
1822
1823 fn from_sub_element_node(
1824 node: syntax_nodes::SubElement,
1825 parent_type: ElementType,
1826 component_child_insertion_point: &mut Option<ChildrenInsertionPoint>,
1827 is_in_legacy_component: bool,
1828 diag: &mut BuildDiagnostics,
1829 tr: &TypeRegister,
1830 ) -> ElementRc {
1831 let mut id = parser::identifier_text(&node).unwrap_or_default();
1832 if matches!(id.as_ref(), "parent" | "self" | "root") {
1833 diag.push_error(
1834 format!("'{id}' is a reserved id"),
1835 &node.child_token(SyntaxKind::Identifier).unwrap(),
1836 );
1837 id = SmolStr::default();
1838 }
1839 Element::from_node(
1840 node.Element(),
1841 id,
1842 parent_type,
1843 component_child_insertion_point,
1844 is_in_legacy_component,
1845 diag,
1846 tr,
1847 )
1848 }
1849
1850 fn from_repeated_node(
1851 node: syntax_nodes::RepeatedElement,
1852 parent: &ElementRc,
1853 component_child_insertion_point: &mut Option<ChildrenInsertionPoint>,
1854 is_in_legacy_component: bool,
1855 diag: &mut BuildDiagnostics,
1856 tr: &TypeRegister,
1857 ) -> ElementRc {
1858 let is_listview = if parent.borrow().base_type.to_string() == "ListView" {
1859 Some(ListViewInfo {
1860 viewport_y: NamedReference::new(parent, SmolStr::new_static("viewport-y")),
1861 viewport_height: NamedReference::new(
1862 parent,
1863 SmolStr::new_static("viewport-height"),
1864 ),
1865 viewport_width: NamedReference::new(parent, SmolStr::new_static("viewport-width")),
1866 listview_height: NamedReference::new(parent, SmolStr::new_static("visible-height")),
1867 listview_width: NamedReference::new(parent, SmolStr::new_static("visible-width")),
1868 })
1869 } else {
1870 None
1871 };
1872 let rei = RepeatedElementInfo {
1873 model: Expression::Uncompiled(node.Expression().into()),
1874 model_data_id: node
1875 .DeclaredIdentifier()
1876 .and_then(|n| parser::identifier_text(&n))
1877 .unwrap_or_default(),
1878 index_id: node
1879 .RepeatedIndex()
1880 .and_then(|r| parser::identifier_text(&r))
1881 .unwrap_or_default(),
1882 is_conditional_element: false,
1883 is_listview,
1884 };
1885 let e = Element::from_sub_element_node(
1886 node.SubElement(),
1887 parent.borrow().base_type.clone(),
1888 component_child_insertion_point,
1889 is_in_legacy_component,
1890 diag,
1891 tr,
1892 );
1893 e.borrow_mut().repeated = Some(rei);
1894 e
1895 }
1896
1897 fn from_conditional_node(
1898 node: syntax_nodes::ConditionalElement,
1899 parent_type: ElementType,
1900 component_child_insertion_point: &mut Option<ChildrenInsertionPoint>,
1901 is_in_legacy_component: bool,
1902 diag: &mut BuildDiagnostics,
1903 tr: &TypeRegister,
1904 ) -> ElementRc {
1905 let rei = RepeatedElementInfo {
1906 model: Expression::Uncompiled(node.Expression().into()),
1907 model_data_id: SmolStr::default(),
1908 index_id: SmolStr::default(),
1909 is_conditional_element: true,
1910 is_listview: None,
1911 };
1912 let e = Element::from_sub_element_node(
1913 node.SubElement(),
1914 parent_type,
1915 component_child_insertion_point,
1916 is_in_legacy_component,
1917 diag,
1918 tr,
1919 );
1920 e.borrow_mut().repeated = Some(rei);
1921 e
1922 }
1923
1924 pub fn lookup_property<'a>(&self, name: &'a str) -> PropertyLookupResult<'a> {
1928 self.property_declarations.get(name).map_or_else(
1929 || {
1930 let mut r = self.base_type.lookup_property(name);
1931 r.is_in_direct_base = r.is_local_to_component;
1932 r.is_local_to_component = false;
1933 r
1934 },
1935 |p| PropertyLookupResult {
1936 resolved_name: name.into(),
1937 property_type: p.property_type.clone(),
1938 property_visibility: p.visibility,
1939 declared_pure: p.pure,
1940 is_local_to_component: true,
1941 is_in_direct_base: false,
1942 builtin_function: None,
1943 },
1944 )
1945 }
1946
1947 fn parse_bindings(
1948 &mut self,
1949 bindings: impl Iterator<Item = (crate::parser::SyntaxToken, SyntaxNode)>,
1950 is_in_legacy_component: bool,
1951 diag: &mut BuildDiagnostics,
1952 ) {
1953 for (name_token, b) in bindings {
1954 let unresolved_name = crate::parser::normalize_identifier(name_token.text());
1955 let lookup_result = self.lookup_property(&unresolved_name);
1956 if !lookup_result.property_type.is_property_type() {
1957 match lookup_result.property_type {
1958 Type::Invalid => {
1959 if self.base_type != ElementType::Error {
1960 diag.push_error(if self.base_type.to_smolstr() == "Empty" {
1961 format!( "Unknown property {unresolved_name}")
1962 } else {
1963 format!( "Unknown property {unresolved_name} in {}", self.base_type)
1964 },
1965 &name_token);
1966 }
1967 }
1968 Type::Callback { .. } => {
1969 diag.push_error(format!("'{unresolved_name}' is a callback. Use `=>` to connect"),
1970 &name_token)
1971 }
1972 _ => diag.push_error(format!(
1973 "Cannot assign to {} in {} because it does not have a valid property type",
1974 unresolved_name, self.base_type,
1975 ),
1976 &name_token),
1977 }
1978 } else if !lookup_result.is_local_to_component
1979 && (lookup_result.property_visibility == PropertyVisibility::Private
1980 || lookup_result.property_visibility == PropertyVisibility::Output)
1981 {
1982 if is_in_legacy_component
1983 && lookup_result.property_visibility == PropertyVisibility::Output
1984 {
1985 diag.push_warning(
1986 format!("Assigning to output property '{unresolved_name}' is deprecated"),
1987 &name_token,
1988 );
1989 } else {
1990 diag.push_error(
1991 format!(
1992 "Cannot assign to {} property '{}'",
1993 lookup_result.property_visibility, unresolved_name
1994 ),
1995 &name_token,
1996 );
1997 }
1998 }
1999
2000 if *lookup_result.resolved_name != *unresolved_name {
2001 diag.push_property_deprecation_warning(
2002 &unresolved_name,
2003 &lookup_result.resolved_name,
2004 &name_token,
2005 );
2006 }
2007
2008 match self.bindings.entry(lookup_result.resolved_name.into()) {
2009 Entry::Occupied(_) => {
2010 diag.push_error("Duplicated property binding".into(), &name_token);
2011 }
2012 Entry::Vacant(entry) => {
2013 entry.insert(BindingExpression::new_uncompiled(b).into());
2014 }
2015 };
2016 }
2017 }
2018
2019 pub fn native_class(&self) -> Option<Rc<NativeClass>> {
2020 let mut base_type = self.base_type.clone();
2021 loop {
2022 match &base_type {
2023 ElementType::Component(component) => {
2024 base_type = component.root_element.clone().borrow().base_type.clone();
2025 }
2026 ElementType::Builtin(builtin) => break Some(builtin.native_class.clone()),
2027 ElementType::Native(native) => break Some(native.clone()),
2028 _ => break None,
2029 }
2030 }
2031 }
2032
2033 pub fn builtin_type(&self) -> Option<Rc<BuiltinElement>> {
2034 let mut base_type = self.base_type.clone();
2035 loop {
2036 match &base_type {
2037 ElementType::Component(component) => {
2038 base_type = component.root_element.clone().borrow().base_type.clone();
2039 }
2040 ElementType::Builtin(builtin) => break Some(builtin.clone()),
2041 _ => break None,
2042 }
2043 }
2044 }
2045
2046 pub fn layout_info_prop(&self, orientation: Orientation) -> Option<&NamedReference> {
2047 self.layout_info_prop.as_ref().map(|prop| match orientation {
2048 Orientation::Horizontal => &prop.0,
2049 Orientation::Vertical => &prop.1,
2050 })
2051 }
2052
2053 pub fn original_name(&self) -> SmolStr {
2055 self.debug
2056 .first()
2057 .and_then(|n| n.node.child_token(parser::SyntaxKind::Identifier))
2058 .map(|n| n.to_smolstr())
2059 .unwrap_or_else(|| self.id.clone())
2060 }
2061
2062 pub fn is_binding_set(self: &Element, property_name: &str, need_explicit: bool) -> bool {
2067 if self.bindings.get(property_name).is_some_and(|b| {
2068 b.borrow().has_binding() && (!need_explicit || b.borrow().priority > 0)
2069 }) {
2070 true
2071 } else if let ElementType::Component(base) = &self.base_type {
2072 base.root_element.borrow().is_binding_set(property_name, need_explicit)
2073 } else {
2074 false
2075 }
2076 }
2077
2078 pub fn set_binding_if_not_set(
2083 &mut self,
2084 property_name: SmolStr,
2085 expression_fn: impl FnOnce() -> Expression,
2086 ) -> bool {
2087 if self.is_binding_set(&property_name, false) {
2088 return false;
2089 }
2090
2091 match self.bindings.entry(property_name) {
2092 Entry::Vacant(vacant_entry) => {
2093 let mut binding: BindingExpression = expression_fn().into();
2094 binding.priority = i32::MAX;
2095 vacant_entry.insert(binding.into());
2096 }
2097 Entry::Occupied(mut existing_entry) => {
2098 let mut binding: BindingExpression = expression_fn().into();
2099 binding.priority = i32::MAX;
2100 existing_entry.get_mut().get_mut().merge_with(&binding);
2101 }
2102 };
2103 true
2104 }
2105
2106 pub fn sub_component(&self) -> Option<&Rc<Component>> {
2107 if self.repeated.is_some() {
2108 None
2109 } else if let ElementType::Component(sub_component) = &self.base_type {
2110 Some(sub_component)
2111 } else {
2112 None
2113 }
2114 }
2115
2116 pub fn element_infos(&self) -> String {
2117 let mut debug_infos = self.debug.clone();
2118 let mut base = self.base_type.clone();
2119 while let ElementType::Component(b) = base {
2120 let elem = b.root_element.borrow();
2121 base = elem.base_type.clone();
2122 debug_infos.extend(elem.debug.iter().cloned());
2123 }
2124
2125 let (infos, _, _) = debug_infos.into_iter().fold(
2126 (String::new(), false, true),
2127 |(mut infos, elem_boundary, first), debug_info| {
2128 if elem_boundary {
2129 infos.push('/');
2130 } else if !first {
2131 infos.push(';');
2132 }
2133
2134 infos.push_str(&debug_info.encoded_element_info());
2135 (infos, debug_info.element_boundary, false)
2136 },
2137 );
2138 infos
2139 }
2140}
2141
2142fn apply_default_type_properties(element: &mut Element) {
2144 if let ElementType::Builtin(builtin_base) = &element.base_type {
2146 for (prop, info) in &builtin_base.properties {
2147 if let BuiltinPropertyDefault::Expr(expr) = &info.default_value {
2148 element.bindings.entry(prop.clone()).or_insert_with(|| {
2149 let mut binding = BindingExpression::from(expr.clone());
2150 binding.priority = i32::MAX;
2151 RefCell::new(binding)
2152 });
2153 }
2154 }
2155 }
2156}
2157
2158fn apply_uses_statement(
2159 e: &ElementRc,
2160 uses_specifier: Option<syntax_nodes::UsesSpecifier>,
2161 tr: &TypeRegister,
2162 diag: &mut BuildDiagnostics,
2163) {
2164 let Some(uses_specifier) = uses_specifier else {
2165 return;
2166 };
2167
2168 if !diag.enable_experimental {
2169 diag.push_error("'uses' is an experimental feature".into(), &uses_specifier);
2170 return;
2171 }
2172
2173 for uses_identifier_node in uses_specifier.UsesIdentifier() {
2174 let uses_statement: UsesStatement = (&uses_identifier_node).into();
2175 let Ok(interface) = uses_statement.lookup_interface(tr, diag) else {
2176 continue;
2177 };
2178
2179 let Some(child) = find_element_by_id(e, &uses_statement.child_id) else {
2180 diag.push_error(
2181 format!("'{}' does not exist", uses_statement.child_id),
2182 &uses_statement.child_id_node(),
2183 );
2184 continue;
2185 };
2186
2187 if !element_implements_interface(&child, &interface, &uses_statement, diag) {
2188 continue;
2189 }
2190
2191 for (prop_name, prop_decl) in &interface.root_element.borrow().property_declarations {
2192 let lookup_result = e.borrow().base_type.lookup_property(prop_name);
2193 if lookup_result.is_valid() {
2194 diag.push_error(
2195 format!(
2196 "Cannot use interface '{}' because property '{}' conflicts with existing property in '{}'",
2197 uses_statement.interface_name, prop_name, e.borrow().base_type
2198 ),
2199 &uses_statement.interface_name_node(),
2200 );
2201 continue;
2202 }
2203
2204 if let Some(existing_property) =
2205 e.borrow_mut().property_declarations.insert(prop_name.clone(), prop_decl.clone())
2206 {
2207 let source = existing_property
2208 .node
2209 .as_ref()
2210 .and_then(|node| node.child_node(SyntaxKind::DeclaredIdentifier))
2211 .and_then(|node| node.child_token(SyntaxKind::Identifier))
2212 .map_or_else(
2213 || parser::NodeOrToken::Node(uses_statement.child_id_node().into()),
2214 parser::NodeOrToken::Token,
2215 );
2216
2217 diag.push_error(
2218 format!(
2219 "Cannot override property '{}' from '{}'",
2220 prop_name, uses_statement.interface_name
2221 ),
2222 &source,
2223 );
2224 continue;
2225 }
2226
2227 if let Some(existing_binding) = e.borrow_mut().bindings.insert(
2228 prop_name.clone(),
2229 BindingExpression::new_two_way(
2230 NamedReference::new(&child, prop_name.clone()).into(),
2231 )
2232 .into(),
2233 ) {
2234 let message = format!(
2235 "Cannot override binding for property '{}' from interface '{}'",
2236 prop_name, uses_statement.interface_name
2237 );
2238 if let Some(location) = &existing_binding.borrow().span {
2239 diag.push_error(message, location);
2240 } else {
2241 diag.push_error(message, &uses_statement.interface_name_node());
2242 }
2243
2244 continue;
2245 }
2246 }
2247 }
2248}
2249
2250fn element_implements_interface(
2252 element: &ElementRc,
2253 interface: &Rc<Component>,
2254 uses_statement: &UsesStatement,
2255 diag: &mut BuildDiagnostics,
2256) -> bool {
2257 let property_matches_interface =
2258 |property: &PropertyLookupResult, interface_declaration: &PropertyDeclaration| -> bool {
2259 property.property_type == interface_declaration.property_type
2260 && property.property_visibility == interface_declaration.visibility
2261 };
2262
2263 for (property_name, property_declaration) in
2264 interface.root_element.borrow().property_declarations.iter()
2265 {
2266 let lookup_result = element.borrow().lookup_property(property_name);
2267 if !property_matches_interface(&lookup_result, property_declaration) {
2268 diag.push_error(
2269 format!(
2270 "{} does not implement {}",
2271 uses_statement.child_id, uses_statement.interface_name
2272 ),
2273 &uses_statement.child_id_node(),
2274 );
2275 return false;
2276 }
2277 }
2278 true
2279}
2280
2281pub fn type_from_node(
2283 node: syntax_nodes::Type,
2284 diag: &mut BuildDiagnostics,
2285 tr: &TypeRegister,
2286) -> Type {
2287 if let Some(qualified_type_node) = node.QualifiedName() {
2288 let qualified_type = QualifiedTypeName::from_node(qualified_type_node.clone());
2289
2290 let prop_type = tr.lookup_qualified(&qualified_type.members);
2291
2292 if prop_type == Type::Invalid && tr.lookup_element(&qualified_type.to_smolstr()).is_err() {
2293 diag.push_error(format!("Unknown type '{qualified_type}'"), &qualified_type_node);
2294 } else if !prop_type.is_property_type() {
2295 diag.push_error(
2296 format!("'{qualified_type}' is not a valid type"),
2297 &qualified_type_node,
2298 );
2299 }
2300 prop_type
2301 } else if let Some(object_node) = node.ObjectType() {
2302 type_struct_from_node(object_node, diag, tr, None)
2303 } else if let Some(array_node) = node.ArrayType() {
2304 Type::Array(Rc::new(type_from_node(array_node.Type(), diag, tr)))
2305 } else {
2306 assert!(diag.has_errors());
2307 Type::Invalid
2308 }
2309}
2310
2311pub fn type_struct_from_node(
2313 object_node: syntax_nodes::ObjectType,
2314 diag: &mut BuildDiagnostics,
2315 tr: &TypeRegister,
2316 name: Option<SmolStr>,
2317) -> Type {
2318 let fields = object_node
2319 .ObjectTypeMember()
2320 .map(|member| {
2321 (
2322 parser::identifier_text(&member).unwrap_or_default(),
2323 type_from_node(member.Type(), diag, tr),
2324 )
2325 })
2326 .collect();
2327 Type::Struct(Rc::new(Struct {
2328 fields,
2329 name: name.map_or(StructName::None, |name| StructName::User { name, node: object_node }),
2330 }))
2331}
2332
2333fn animation_element_from_node(
2334 anim: &syntax_nodes::PropertyAnimation,
2335 prop_name: &syntax_nodes::QualifiedName,
2336 prop_type: Type,
2337 diag: &mut BuildDiagnostics,
2338 tr: &TypeRegister,
2339) -> Option<ElementRc> {
2340 let anim_type = tr.property_animation_type_for_property(prop_type);
2341 if !matches!(anim_type, ElementType::Builtin(..)) {
2342 diag.push_error(
2343 format!(
2344 "'{}' is not a property that can be animated",
2345 prop_name.text().to_string().trim()
2346 ),
2347 prop_name,
2348 );
2349 None
2350 } else {
2351 let mut anim_element =
2352 Element { id: "".into(), base_type: anim_type, ..Default::default() };
2353 anim_element.parse_bindings(
2354 anim.Binding().filter_map(|b| {
2355 Some((b.child_token(SyntaxKind::Identifier)?, b.BindingExpression().into()))
2356 }),
2357 false,
2358 diag,
2359 );
2360
2361 apply_default_type_properties(&mut anim_element);
2362
2363 Some(Rc::new(RefCell::new(anim_element)))
2364 }
2365}
2366
2367#[derive(Default, Debug, Clone)]
2368pub struct QualifiedTypeName {
2369 pub members: Vec<SmolStr>,
2370}
2371
2372impl QualifiedTypeName {
2373 pub fn from_node(node: syntax_nodes::QualifiedName) -> Self {
2374 debug_assert_eq!(node.kind(), SyntaxKind::QualifiedName);
2375 let members = node
2376 .children_with_tokens()
2377 .filter(|n| n.kind() == SyntaxKind::Identifier)
2378 .filter_map(|x| x.as_token().map(|x| crate::parser::normalize_identifier(x.text())))
2379 .collect();
2380 Self { members }
2381 }
2382}
2383
2384impl Display for QualifiedTypeName {
2385 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2386 write!(f, "{}", self.members.join("."))
2387 }
2388}
2389
2390fn lookup_property_from_qualified_name_for_state(
2393 node: syntax_nodes::QualifiedName,
2394 r: &ElementRc,
2395 diag: &mut BuildDiagnostics,
2396) -> Option<(NamedReference, Type)> {
2397 let qualname = QualifiedTypeName::from_node(node.clone());
2398 match qualname.members.as_slice() {
2399 [unresolved_prop_name] => {
2400 let lookup_result = r.borrow().lookup_property(unresolved_prop_name.as_ref());
2401 if !lookup_result.property_type.is_property_type() {
2402 diag.push_error(format!("'{qualname}' is not a valid property"), &node);
2403 } else if !lookup_result.is_valid_for_assignment() {
2404 diag.push_error(
2405 format!(
2406 "'{}' cannot be set in a state because it is {}",
2407 qualname, lookup_result.property_visibility
2408 ),
2409 &node,
2410 );
2411 }
2412 Some((
2413 NamedReference::new(r, lookup_result.resolved_name.to_smolstr()),
2414 lookup_result.property_type,
2415 ))
2416 }
2417 [elem_id, unresolved_prop_name] => {
2418 if let Some(element) = find_element_by_id(r, elem_id.as_ref()) {
2419 let lookup_result = element.borrow().lookup_property(unresolved_prop_name.as_ref());
2420 if !lookup_result.is_valid() {
2421 diag.push_error(
2422 format!("'{unresolved_prop_name}' not found in '{elem_id}'"),
2423 &node,
2424 );
2425 } else if !lookup_result.is_valid_for_assignment() {
2426 diag.push_error(
2427 format!(
2428 "'{}' cannot be set in a state because it is {}",
2429 qualname, lookup_result.property_visibility
2430 ),
2431 &node,
2432 );
2433 }
2434 Some((
2435 NamedReference::new(&element, lookup_result.resolved_name.to_smolstr()),
2436 lookup_result.property_type,
2437 ))
2438 } else {
2439 diag.push_error(format!("'{elem_id}' is not a valid element id"), &node);
2440 None
2441 }
2442 }
2443 _ => {
2444 diag.push_error(format!("'{qualname}' is not a valid property"), &node);
2445 None
2446 }
2447 }
2448}
2449
2450fn find_element_by_id(e: &ElementRc, name: &str) -> Option<ElementRc> {
2452 if e.borrow().id == name {
2453 return Some(e.clone());
2454 }
2455 for x in &e.borrow().children {
2456 if x.borrow().repeated.is_some() {
2457 continue;
2458 }
2459 if let Some(x) = find_element_by_id(x, name) {
2460 return Some(x);
2461 }
2462 }
2463
2464 None
2465}
2466
2467pub fn find_parent_element(e: &ElementRc) -> Option<ElementRc> {
2470 fn recurse(base: &ElementRc, e: &ElementRc) -> Option<ElementRc> {
2471 for child in &base.borrow().children {
2472 if Rc::ptr_eq(child, e) {
2473 return Some(base.clone());
2474 }
2475 if let Some(x) = recurse(child, e) {
2476 return Some(x);
2477 }
2478 }
2479 None
2480 }
2481
2482 let root = e.borrow().enclosing_component.upgrade().unwrap().root_element.clone();
2483 if Rc::ptr_eq(&root, e) {
2484 return None;
2485 }
2486 recurse(&root, e)
2487}
2488
2489pub fn recurse_elem<State>(
2493 elem: &ElementRc,
2494 state: &State,
2495 vis: &mut impl FnMut(&ElementRc, &State) -> State,
2496) {
2497 let state = vis(elem, state);
2498 for sub in &elem.borrow().children {
2499 recurse_elem(sub, &state, vis);
2500 }
2501}
2502
2503pub fn recurse_elem_including_sub_components<State>(
2505 component: &Component,
2506 state: &State,
2507 vis: &mut impl FnMut(&ElementRc, &State) -> State,
2508) {
2509 recurse_elem(&component.root_element, state, &mut |elem, state| {
2510 debug_assert!(std::ptr::eq(
2511 component as *const Component,
2512 (&*elem.borrow().enclosing_component.upgrade().unwrap()) as *const Component
2513 ));
2514 if elem.borrow().repeated.is_some()
2515 && let ElementType::Component(base) = &elem.borrow().base_type
2516 && base.parent_element.upgrade().is_some()
2517 {
2518 recurse_elem_including_sub_components(base, state, vis);
2519 }
2520 vis(elem, state)
2521 });
2522 component
2523 .popup_windows
2524 .borrow()
2525 .iter()
2526 .for_each(|p| recurse_elem_including_sub_components(&p.component, state, vis));
2527 component
2528 .menu_item_tree
2529 .borrow()
2530 .iter()
2531 .for_each(|c| recurse_elem_including_sub_components(c, state, vis));
2532}
2533
2534pub fn recurse_elem_no_borrow<State>(
2536 elem: &ElementRc,
2537 state: &State,
2538 vis: &mut impl FnMut(&ElementRc, &State) -> State,
2539) {
2540 let state = vis(elem, state);
2541 let children = elem.borrow().children.clone();
2542 for sub in &children {
2543 recurse_elem_no_borrow(sub, &state, vis);
2544 }
2545}
2546
2547pub fn recurse_elem_including_sub_components_no_borrow<State>(
2549 component: &Component,
2550 state: &State,
2551 vis: &mut impl FnMut(&ElementRc, &State) -> State,
2552) {
2553 recurse_elem_no_borrow(&component.root_element, state, &mut |elem, state| {
2554 let base = if elem.borrow().repeated.is_some() {
2555 if let ElementType::Component(base) = &elem.borrow().base_type {
2556 if base.parent_element.upgrade().is_some() {
2557 Some(base.clone())
2558 } else {
2559 None
2561 }
2562 } else {
2563 None
2564 }
2565 } else {
2566 None
2567 };
2568 if let Some(base) = base {
2569 recurse_elem_including_sub_components_no_borrow(&base, state, vis);
2570 }
2571 vis(elem, state)
2572 });
2573 component
2574 .popup_windows
2575 .borrow()
2576 .iter()
2577 .for_each(|p| recurse_elem_including_sub_components_no_borrow(&p.component, state, vis));
2578 component
2579 .menu_item_tree
2580 .borrow()
2581 .iter()
2582 .for_each(|c| recurse_elem_including_sub_components_no_borrow(c, state, vis));
2583}
2584
2585pub fn visit_element_expressions(
2591 elem: &ElementRc,
2592 mut vis: impl FnMut(&mut Expression, Option<&str>, &dyn Fn() -> Type),
2593) {
2594 fn visit_element_expressions_simple(
2595 elem: &ElementRc,
2596 vis: &mut impl FnMut(&mut Expression, Option<&str>, &dyn Fn() -> Type),
2597 ) {
2598 for (name, expr) in &elem.borrow().bindings {
2599 vis(&mut expr.borrow_mut(), Some(name.as_str()), &|| {
2600 elem.borrow().lookup_property(name).property_type
2601 });
2602
2603 match &mut expr.borrow_mut().animation {
2604 Some(PropertyAnimation::Static(e)) => visit_element_expressions_simple(e, vis),
2605 Some(PropertyAnimation::Transition { animations, state_ref }) => {
2606 vis(state_ref, None, &|| Type::Int32);
2607 for a in animations {
2608 visit_element_expressions_simple(&a.animation, vis)
2609 }
2610 }
2611 None => (),
2612 }
2613 }
2614 }
2615
2616 let repeated = elem
2617 .borrow_mut()
2618 .repeated
2619 .as_mut()
2620 .map(|r| (std::mem::take(&mut r.model), r.is_conditional_element));
2621 if let Some((mut model, is_cond)) = repeated {
2622 vis(&mut model, None, &|| if is_cond { Type::Bool } else { Type::Model });
2623 elem.borrow_mut().repeated.as_mut().unwrap().model = model;
2624 }
2625 visit_element_expressions_simple(elem, &mut vis);
2626
2627 for expr in elem.borrow().change_callbacks.values() {
2628 for expr in expr.borrow_mut().iter_mut() {
2629 vis(expr, Some("$change callback$"), &|| Type::Void);
2630 }
2631 }
2632
2633 let mut states = std::mem::take(&mut elem.borrow_mut().states);
2634 for s in &mut states {
2635 if let Some(cond) = s.condition.as_mut() {
2636 vis(cond, None, &|| Type::Bool)
2637 }
2638 for (ne, e, _) in &mut s.property_changes {
2639 vis(e, Some(ne.name()), &|| {
2640 ne.element().borrow().lookup_property(ne.name()).property_type
2641 });
2642 }
2643 }
2644 elem.borrow_mut().states = states;
2645
2646 let mut transitions = std::mem::take(&mut elem.borrow_mut().transitions);
2647 for t in &mut transitions {
2648 for (_, _, a) in &mut t.property_animations {
2649 visit_element_expressions_simple(a, &mut vis);
2650 }
2651 }
2652 elem.borrow_mut().transitions = transitions;
2653
2654 let component = elem.borrow().enclosing_component.upgrade().unwrap();
2655 if Rc::ptr_eq(&component.root_element, elem) {
2656 for e in component.init_code.borrow_mut().iter_mut() {
2657 vis(e, None, &|| Type::Void);
2658 }
2659 }
2660}
2661
2662pub fn visit_named_references_in_expression(
2663 expr: &mut Expression,
2664 vis: &mut impl FnMut(&mut NamedReference),
2665) {
2666 expr.visit_mut(|sub| visit_named_references_in_expression(sub, vis));
2667 match expr {
2668 Expression::PropertyReference(r) => vis(r),
2669 Expression::FunctionCall {
2670 function: Callable::Callback(r) | Callable::Function(r),
2671 ..
2672 } => vis(r),
2673 Expression::LayoutCacheAccess { layout_cache_prop, .. } => vis(layout_cache_prop),
2674 Expression::OrganizeGridLayout(l) => l.visit_named_references(vis),
2675 Expression::ComputeLayoutInfo(l, _) => l.visit_named_references(vis),
2676 Expression::ComputeGridLayoutInfo { layout_organized_data_prop, layout, .. } => {
2677 vis(layout_organized_data_prop);
2678 layout.visit_named_references(vis);
2679 }
2680 Expression::SolveLayout(l, _) => l.visit_named_references(vis),
2681 Expression::SolveGridLayout { layout_organized_data_prop, layout, .. } => {
2682 vis(layout_organized_data_prop);
2683 layout.visit_named_references(vis);
2684 }
2685 Expression::RepeaterModelReference { element }
2688 | Expression::RepeaterIndexReference { element } => {
2689 let mut nc =
2691 NamedReference::new(&element.upgrade().unwrap(), SmolStr::new_static("$model"));
2692 vis(&mut nc);
2693 debug_assert!(nc.element().borrow().repeated.is_some());
2694 *element = Rc::downgrade(&nc.element());
2695 }
2696 _ => {}
2697 }
2698}
2699
2700pub fn visit_all_named_references_in_element(
2703 elem: &ElementRc,
2704 mut vis: impl FnMut(&mut NamedReference),
2705) {
2706 visit_element_expressions(elem, |expr, _, _| {
2707 visit_named_references_in_expression(expr, &mut vis)
2708 });
2709 let mut states = std::mem::take(&mut elem.borrow_mut().states);
2710 for s in &mut states {
2711 for (r, _, _) in &mut s.property_changes {
2712 vis(r);
2713 }
2714 }
2715 elem.borrow_mut().states = states;
2716 let mut transitions = std::mem::take(&mut elem.borrow_mut().transitions);
2717 for t in &mut transitions {
2718 for (r, _, _) in &mut t.property_animations {
2719 vis(r)
2720 }
2721 }
2722 elem.borrow_mut().transitions = transitions;
2723 let mut repeated = std::mem::take(&mut elem.borrow_mut().repeated);
2724 if let Some(r) = &mut repeated
2725 && let Some(lv) = &mut r.is_listview
2726 {
2727 vis(&mut lv.viewport_y);
2728 vis(&mut lv.viewport_height);
2729 vis(&mut lv.viewport_width);
2730 vis(&mut lv.listview_height);
2731 vis(&mut lv.listview_width);
2732 }
2733 elem.borrow_mut().repeated = repeated;
2734 let mut layout_info_prop = std::mem::take(&mut elem.borrow_mut().layout_info_prop);
2735 layout_info_prop.as_mut().map(|(h, b)| (vis(h), vis(b)));
2736 elem.borrow_mut().layout_info_prop = layout_info_prop;
2737 let mut debug = std::mem::take(&mut elem.borrow_mut().debug);
2738 for d in debug.iter_mut() {
2739 if let Some(l) = d.layout.as_mut() {
2740 l.visit_named_references(&mut vis)
2741 }
2742 }
2743 elem.borrow_mut().debug = debug;
2744
2745 let mut accessibility_props = std::mem::take(&mut elem.borrow_mut().accessibility_props);
2746 accessibility_props.0.iter_mut().for_each(|(_, x)| vis(x));
2747 elem.borrow_mut().accessibility_props = accessibility_props;
2748
2749 let geometry_props = elem.borrow_mut().geometry_props.take();
2750 if let Some(mut geometry_props) = geometry_props {
2751 vis(&mut geometry_props.x);
2752 vis(&mut geometry_props.y);
2753 vis(&mut geometry_props.width);
2754 vis(&mut geometry_props.height);
2755 elem.borrow_mut().geometry_props = Some(geometry_props);
2756 }
2757
2758 for expr in elem.borrow().bindings.values() {
2760 for nr in &mut expr.borrow_mut().two_way_bindings {
2761 vis(&mut nr.property);
2762 }
2763 }
2764
2765 let mut property_declarations = std::mem::take(&mut elem.borrow_mut().property_declarations);
2766 for pd in property_declarations.values_mut() {
2767 pd.is_alias.as_mut().map(&mut vis);
2768 }
2769 elem.borrow_mut().property_declarations = property_declarations;
2770
2771 let grid_layout_cell = elem.borrow_mut().grid_layout_cell.take();
2773 if let Some(grid_layout_cell) = grid_layout_cell {
2774 grid_layout_cell.borrow_mut().visit_named_references(&mut vis);
2775 elem.borrow_mut().grid_layout_cell = Some(grid_layout_cell);
2776 }
2777}
2778
2779pub fn visit_all_named_references(
2781 component: &Component,
2782 vis: &mut impl FnMut(&mut NamedReference),
2783) {
2784 recurse_elem_including_sub_components_no_borrow(
2785 component,
2786 &Weak::new(),
2787 &mut |elem, parent_compo| {
2788 visit_all_named_references_in_element(elem, |nr| vis(nr));
2789 let compo = elem.borrow().enclosing_component.clone();
2790 if !Weak::ptr_eq(parent_compo, &compo) {
2791 let compo = compo.upgrade().unwrap();
2792 compo.root_constraints.borrow_mut().visit_named_references(vis);
2793 compo.popup_windows.borrow_mut().iter_mut().for_each(|p| {
2794 vis(&mut p.x);
2795 vis(&mut p.y);
2796 });
2797 compo.timers.borrow_mut().iter_mut().for_each(|t| {
2798 vis(&mut t.interval);
2799 vis(&mut t.triggered);
2800 vis(&mut t.running);
2801 });
2802 for o in compo.optimized_elements.borrow().iter() {
2803 visit_element_expressions(o, |expr, _, _| {
2804 visit_named_references_in_expression(expr, vis)
2805 });
2806 }
2807 }
2808 compo
2809 },
2810 );
2811}
2812
2813pub fn visit_all_expressions(
2817 component: &Component,
2818 mut vis: impl FnMut(&mut Expression, &dyn Fn() -> Type),
2819) {
2820 recurse_elem_including_sub_components(component, &Weak::new(), &mut |elem, parent_compo| {
2821 visit_element_expressions(elem, |expr, _, ty| vis(expr, ty));
2822 let compo = elem.borrow().enclosing_component.clone();
2823 if !Weak::ptr_eq(parent_compo, &compo) {
2824 let compo = compo.upgrade().unwrap();
2825 for o in compo.optimized_elements.borrow().iter() {
2826 visit_element_expressions(o, |expr, _, ty| vis(expr, ty));
2827 }
2828 }
2829 compo
2830 })
2831}
2832
2833#[derive(Debug, Clone)]
2834pub struct State {
2835 pub id: SmolStr,
2836 pub condition: Option<Expression>,
2837 pub property_changes: Vec<(NamedReference, Expression, syntax_nodes::StatePropertyChange)>,
2838}
2839
2840#[derive(Debug, Clone)]
2841pub struct Transition {
2842 pub direction: TransitionDirection,
2843 pub state_id: SmolStr,
2844 pub property_animations: Vec<(NamedReference, SourceLocation, ElementRc)>,
2845 pub node: syntax_nodes::Transition,
2846}
2847
2848impl Transition {
2849 fn from_node(
2850 trs: syntax_nodes::Transition,
2851 r: &ElementRc,
2852 tr: &TypeRegister,
2853 diag: &mut BuildDiagnostics,
2854 ) -> Transition {
2855 if let Some(star) = trs.child_token(SyntaxKind::Star) {
2856 diag.push_error("catch-all not yet implemented".into(), &star);
2857 };
2858 let direction_text = trs
2859 .first_child_or_token()
2860 .and_then(|t| t.as_token().map(|tok| tok.text().to_string()))
2861 .unwrap_or_default();
2862
2863 Transition {
2864 direction: match direction_text.as_str() {
2865 "in" => TransitionDirection::In,
2866 "out" => TransitionDirection::Out,
2867 "in-out" => TransitionDirection::InOut,
2868 "in_out" => TransitionDirection::InOut,
2869 _ => {
2870 unreachable!("Unknown transition direction: '{}'", direction_text);
2871 }
2872 },
2873 state_id: trs
2874 .DeclaredIdentifier()
2875 .and_then(|x| parser::identifier_text(&x))
2876 .unwrap_or_default(),
2877 property_animations: trs
2878 .PropertyAnimation()
2879 .flat_map(|pa| pa.QualifiedName().map(move |qn| (pa.clone(), qn)))
2880 .filter_map(|(pa, qn)| {
2881 lookup_property_from_qualified_name_for_state(qn.clone(), r, diag).and_then(
2882 |(ne, prop_type)| {
2883 animation_element_from_node(&pa, &qn, prop_type, diag, tr)
2884 .map(|anim_element| (ne, qn.to_source_location(), anim_element))
2885 },
2886 )
2887 })
2888 .collect(),
2889 node: trs.clone(),
2890 }
2891 }
2892}
2893
2894#[derive(Clone, Debug, derive_more::Deref)]
2895pub struct ExportedName {
2896 #[deref]
2897 pub name: SmolStr, pub name_ident: SyntaxNode,
2899}
2900
2901impl ExportedName {
2902 pub fn original_name(&self) -> SmolStr {
2903 self.name_ident
2904 .child_token(parser::SyntaxKind::Identifier)
2905 .map(|n| n.to_smolstr())
2906 .unwrap_or_else(|| self.name.clone())
2907 }
2908
2909 pub fn from_export_specifier(
2910 export_specifier: &syntax_nodes::ExportSpecifier,
2911 ) -> (SmolStr, ExportedName) {
2912 let internal_name =
2913 parser::identifier_text(&export_specifier.ExportIdentifier()).unwrap_or_default();
2914
2915 let (name, name_ident): (SmolStr, SyntaxNode) = export_specifier
2916 .ExportName()
2917 .and_then(|ident| {
2918 parser::identifier_text(&ident).map(|text| (text, ident.clone().into()))
2919 })
2920 .unwrap_or_else(|| (internal_name.clone(), export_specifier.ExportIdentifier().into()));
2921 (internal_name, ExportedName { name, name_ident })
2922 }
2923}
2924
2925#[derive(Default, Debug, derive_more::Deref)]
2926pub struct Exports {
2927 #[deref]
2928 components_or_types: Vec<(ExportedName, Either<Rc<Component>, Type>)>,
2929}
2930
2931impl Exports {
2932 pub fn from_node(
2933 doc: &syntax_nodes::Document,
2934 inner_components: &[Rc<Component>],
2935 type_registry: &TypeRegister,
2936 diag: &mut BuildDiagnostics,
2937 ) -> Self {
2938 let resolve_export_to_inner_component_or_import =
2939 |internal_name: &str, internal_name_node: &dyn Spanned, diag: &mut BuildDiagnostics| {
2940 if let Ok(ElementType::Component(c)) = type_registry.lookup_element(internal_name) {
2941 Some(Either::Left(c))
2942 } else if let ty @ Type::Struct { .. } | ty @ Type::Enumeration(_) =
2943 type_registry.lookup(internal_name)
2944 {
2945 Some(Either::Right(ty))
2946 } else if type_registry.lookup_element(internal_name).is_ok()
2947 || type_registry.lookup(internal_name) != Type::Invalid
2948 {
2949 diag.push_error(
2950 format!("Cannot export '{internal_name}' because it is not a component",),
2951 internal_name_node,
2952 );
2953 None
2954 } else {
2955 diag.push_error(format!("'{internal_name}' not found",), internal_name_node);
2956 None
2957 }
2958 };
2959
2960 let mut exports_with_duplicates: Vec<(ExportedName, Either<Rc<Component>, Type>)> =
2963 Vec::new();
2964
2965 exports_with_duplicates.extend(
2967 doc.ExportsList()
2968 .filter(|exports| exports.ExportModule().is_none())
2970 .flat_map(|exports| exports.ExportSpecifier())
2971 .filter_map(|export_specifier| {
2972 let (internal_name, exported_name) =
2973 ExportedName::from_export_specifier(&export_specifier);
2974 Some((
2975 exported_name,
2976 resolve_export_to_inner_component_or_import(
2977 &internal_name,
2978 &export_specifier.ExportIdentifier(),
2979 diag,
2980 )?,
2981 ))
2982 }),
2983 );
2984
2985 exports_with_duplicates.extend(
2987 doc.ExportsList().flat_map(|exports| exports.Component()).filter_map(|component| {
2988 let name_ident: SyntaxNode = component.DeclaredIdentifier().into();
2989 let name =
2990 parser::identifier_text(&component.DeclaredIdentifier()).unwrap_or_else(|| {
2991 debug_assert!(diag.has_errors());
2992 SmolStr::default()
2993 });
2994
2995 let compo_or_type =
2996 resolve_export_to_inner_component_or_import(&name, &name_ident, diag)?;
2997
2998 Some((ExportedName { name, name_ident }, compo_or_type))
2999 }),
3000 );
3001
3002 exports_with_duplicates.extend(
3004 doc.ExportsList()
3005 .flat_map(|exports| {
3006 exports
3007 .StructDeclaration()
3008 .map(|st| st.DeclaredIdentifier())
3009 .chain(exports.EnumDeclaration().map(|en| en.DeclaredIdentifier()))
3010 })
3011 .filter_map(|name_ident| {
3012 let name = parser::identifier_text(&name_ident).unwrap_or_else(|| {
3013 debug_assert!(diag.has_errors());
3014 SmolStr::default()
3015 });
3016
3017 let name_ident = name_ident.into();
3018
3019 let compo_or_type =
3020 resolve_export_to_inner_component_or_import(&name, &name_ident, diag)?;
3021
3022 Some((ExportedName { name, name_ident }, compo_or_type))
3023 }),
3024 );
3025
3026 exports_with_duplicates.sort_by(|(a, _), (b, _)| a.name.cmp(&b.name));
3027
3028 let mut sorted_deduped_exports = Vec::with_capacity(exports_with_duplicates.len());
3029 let mut it = exports_with_duplicates.into_iter().peekable();
3030 while let Some((exported_name, compo_or_type)) = it.next() {
3031 let mut warning_issued_on_first_occurrence = false;
3032
3033 while it.peek().map(|(name, _)| &name.name) == Some(&exported_name.name) {
3035 let message = format!("Duplicated export '{}'", exported_name.name);
3036
3037 if !warning_issued_on_first_occurrence {
3038 diag.push_error(message.clone(), &exported_name.name_ident);
3039 warning_issued_on_first_occurrence = true;
3040 }
3041
3042 let duplicate_loc = it.next().unwrap().0.name_ident;
3043 diag.push_error(message.clone(), &duplicate_loc);
3044 }
3045
3046 sorted_deduped_exports.push((exported_name, compo_or_type));
3047 }
3048
3049 if let Some(last_compo) = inner_components.last() {
3050 let name = last_compo.id.clone();
3051 if last_compo.is_global() {
3052 if sorted_deduped_exports.is_empty() {
3053 diag.push_warning("Global singleton is implicitly marked for export. This is deprecated and it should be explicitly exported".into(), &last_compo.node.as_ref().map(|n| n.to_source_location()));
3054 sorted_deduped_exports.push((
3055 ExportedName { name, name_ident: doc.clone().into() },
3056 Either::Left(last_compo.clone()),
3057 ))
3058 }
3059 } else if !sorted_deduped_exports
3060 .iter()
3061 .any(|e| e.1.as_ref().left().is_some_and(|c| !c.is_global()))
3062 {
3063 diag.push_warning("Component is implicitly marked for export. This is deprecated and it should be explicitly exported".into(), &last_compo.node.as_ref().map(|n| n.to_source_location()));
3064 let insert_pos = sorted_deduped_exports
3065 .partition_point(|(existing_export, _)| existing_export.name <= name);
3066 sorted_deduped_exports.insert(
3067 insert_pos,
3068 (
3069 ExportedName { name, name_ident: doc.clone().into() },
3070 Either::Left(last_compo.clone()),
3071 ),
3072 )
3073 }
3074 }
3075 Self { components_or_types: sorted_deduped_exports }
3076 }
3077
3078 pub fn add_reexports(
3079 &mut self,
3080 other_exports: impl IntoIterator<Item = (ExportedName, Either<Rc<Component>, Type>)>,
3081 diag: &mut BuildDiagnostics,
3082 ) {
3083 for export in other_exports {
3084 match self.components_or_types.binary_search_by(|entry| entry.0.cmp(&export.0)) {
3085 Ok(_) => {
3086 diag.push_warning(
3087 format!(
3088 "'{}' is already exported in this file; it will not be re-exported",
3089 &*export.0
3090 ),
3091 &export.0.name_ident,
3092 );
3093 }
3094 Err(insert_pos) => {
3095 self.components_or_types.insert(insert_pos, export);
3096 }
3097 }
3098 }
3099 }
3100
3101 pub fn find(&self, name: &str) -> Option<Either<Rc<Component>, Type>> {
3102 self.components_or_types
3103 .binary_search_by(|(exported_name, _)| exported_name.as_str().cmp(name))
3104 .ok()
3105 .map(|index| self.components_or_types[index].1.clone())
3106 }
3107
3108 pub fn retain(
3109 &mut self,
3110 func: impl FnMut(&mut (ExportedName, Either<Rc<Component>, Type>)) -> bool,
3111 ) {
3112 self.components_or_types.retain_mut(func)
3113 }
3114
3115 pub(crate) fn snapshot(&self, snapshotter: &mut crate::typeloader::Snapshotter) -> Self {
3116 let components_or_types = self
3117 .components_or_types
3118 .iter()
3119 .map(|(en, either)| {
3120 let en = en.clone();
3121 let either = match either {
3122 itertools::Either::Left(l) => itertools::Either::Left({
3123 Weak::upgrade(&snapshotter.use_component(l))
3124 .expect("Component should cleanly upgrade here")
3125 }),
3126 itertools::Either::Right(r) => itertools::Either::Right(r.clone()),
3127 };
3128 (en, either)
3129 })
3130 .collect();
3131
3132 Self { components_or_types }
3133 }
3134}
3135
3136impl std::iter::IntoIterator for Exports {
3137 type Item = (ExportedName, Either<Rc<Component>, Type>);
3138
3139 type IntoIter = std::vec::IntoIter<Self::Item>;
3140
3141 fn into_iter(self) -> Self::IntoIter {
3142 self.components_or_types.into_iter()
3143 }
3144}
3145
3146pub fn inject_element_as_repeated_element(repeated_element: &ElementRc, new_root: ElementRc) {
3150 let component = repeated_element.borrow().base_type.as_component().clone();
3151 debug_assert_eq!(Rc::strong_count(&component), 2);
3155 let old_root = &component.root_element;
3156
3157 adjust_geometry_for_injected_parent(&new_root, old_root);
3158
3159 let mut elements_with_enclosing_component_reference = Vec::new();
3161 recurse_elem(old_root, &(), &mut |element: &ElementRc, _| {
3162 if let Some(enclosing_component) = element.borrow().enclosing_component.upgrade()
3163 && Rc::ptr_eq(&enclosing_component, &component)
3164 {
3165 elements_with_enclosing_component_reference.push(element.clone());
3166 }
3167 });
3168 elements_with_enclosing_component_reference
3169 .extend_from_slice(component.optimized_elements.borrow().as_slice());
3170 elements_with_enclosing_component_reference.push(new_root.clone());
3171
3172 new_root.borrow_mut().child_of_layout =
3173 std::mem::replace(&mut old_root.borrow_mut().child_of_layout, false);
3174 let layout_info_prop = old_root.borrow().layout_info_prop.clone().or_else(|| {
3175 let li_v = crate::layout::create_new_prop(
3177 &new_root,
3178 SmolStr::new_static("layoutinfo-v"),
3179 crate::typeregister::layout_info_type().into(),
3180 );
3181 let li_h = crate::layout::create_new_prop(
3182 &new_root,
3183 SmolStr::new_static("layoutinfo-h"),
3184 crate::typeregister::layout_info_type().into(),
3185 );
3186 let expr_h = crate::layout::implicit_layout_info_call(old_root, Orientation::Horizontal);
3187 let expr_v = crate::layout::implicit_layout_info_call(old_root, Orientation::Vertical);
3188 let expr_v =
3189 BindingExpression::new_with_span(expr_v, old_root.borrow().to_source_location());
3190 li_v.element().borrow_mut().bindings.insert(li_v.name().clone(), expr_v.into());
3191 let expr_h =
3192 BindingExpression::new_with_span(expr_h, old_root.borrow().to_source_location());
3193 li_h.element().borrow_mut().bindings.insert(li_h.name().clone(), expr_h.into());
3194 Some((li_h.clone(), li_v.clone()))
3195 });
3196 new_root.borrow_mut().layout_info_prop = layout_info_prop;
3197
3198 drop(std::mem::take(&mut repeated_element.borrow_mut().base_type));
3201
3202 debug_assert_eq!(Rc::strong_count(&component), 1);
3203
3204 let mut component = Rc::try_unwrap(component).expect("internal compiler error: more than one strong reference left to repeated component when lowering shadow properties");
3205
3206 let old_root = std::mem::replace(&mut component.root_element, new_root.clone());
3207 new_root.borrow_mut().children.push(old_root);
3208
3209 let component = Rc::new(component);
3210 repeated_element.borrow_mut().base_type = ElementType::Component(component.clone());
3211
3212 for elem in elements_with_enclosing_component_reference {
3213 elem.borrow_mut().enclosing_component = Rc::downgrade(&component);
3214 }
3215}
3216
3217pub fn adjust_geometry_for_injected_parent(injected_parent: &ElementRc, old_elem: &ElementRc) {
3220 let mut injected_parent_mut = injected_parent.borrow_mut();
3221 injected_parent_mut.bindings.insert(
3222 "z".into(),
3223 RefCell::new(BindingExpression::new_two_way(
3224 NamedReference::new(old_elem, SmolStr::new_static("z")).into(),
3225 )),
3226 );
3227 injected_parent_mut.property_declarations.insert(
3229 "dummy".into(),
3230 PropertyDeclaration { property_type: Type::LogicalLength, ..Default::default() },
3231 );
3232 let mut old_elem_mut = old_elem.borrow_mut();
3233 injected_parent_mut.default_fill_parent = std::mem::take(&mut old_elem_mut.default_fill_parent);
3234 injected_parent_mut.geometry_props.clone_from(&old_elem_mut.geometry_props);
3235 drop(injected_parent_mut);
3236 old_elem_mut.geometry_props.as_mut().unwrap().x =
3237 NamedReference::new(injected_parent, SmolStr::new_static("dummy"));
3238 old_elem_mut.geometry_props.as_mut().unwrap().y =
3239 NamedReference::new(injected_parent, SmolStr::new_static("dummy"));
3240}