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