i_slint_compiler/
object_tree.rs

1// Copyright © SixtyFPS GmbH <info@slint.dev>
2// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0
3
4/*!
5 This module contains the intermediate representation of the code in the form of an object tree
6*/
7
8// cSpell: ignore qualname
9
10use 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()); // error should have been reported at parsing time
39                continue;
40            }
41        }
42    };
43}
44
45/// The full document (a complete file)
46#[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    /// A list of paths to .ttf/.ttc files that are supposed to be registered on
53    /// startup for custom font use.
54    pub custom_fonts: Vec<(SmolStr, crate::parser::SyntaxToken)>,
55    pub exports: Exports,
56    pub imports: Vec<ImportedTypes>,
57
58    /// Map of resources that should be embedded in the generated code, indexed by their absolute path on
59    /// disk on the build system
60    pub embedded_file_resources:
61        RefCell<HashMap<SmolStr, crate::embedded_resources::EmbeddedResources>>,
62
63    /// The list of used extra types used recursively.
64    pub used_types: RefCell<UsedSubTypes>,
65
66    /// The popup_menu_impl
67    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                    // Assume remote urls are valid, we need to load them at run-time (which we currently don't). For
206                    // local paths we should try to verify the existence and let the developer know ASAP.
207                    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            // Don't warn about these for now - detecting their use can only be done after the resolve_expressions
242            // pass.
243            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    /// This is the component that is going to be instantiated by the interpreter
273    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    /// visit all root and used component (including globals)
283    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/// Used sub types for a root component
319#[derive(Debug, Default)]
320pub struct UsedSubTypes {
321    /// All the globals used by the component and its children.
322    pub globals: Vec<Rc<Component>>,
323    /// All the structs and enums used by the component and its children.
324    pub structs_and_enums: Vec<Type>,
325    /// All the sub components use by this components and its children,
326    /// and the amount of time it is used
327    pub sub_components: Vec<Rc<Component>>,
328}
329
330#[derive(Debug, Default, Clone)]
331pub struct InitCode {
332    // Code from init callbacks collected from elements
333    pub constructor_code: Vec<Expression>,
334    /// Code to set the initial focus via forward-focus on the Window
335    pub focus_setting_code: Vec<Expression>,
336    /// Code to register embedded fonts.
337    pub font_registration_code: Vec<Expression>,
338
339    /// Code inserted from inlined components, ordered by offset of the place where it was inlined from. This way
340    /// we can preserve the order across multiple inlining passes.
341    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/// A component is a type in the language which can be instantiated,
362/// Or is materialized for repeated expression.
363#[derive(Default, Debug)]
364pub struct Component {
365    pub node: Option<SyntaxNode>,
366    pub id: SmolStr,
367    pub root_element: ElementRc,
368
369    /// The parent element within the parent component if this component represents a repeated element
370    pub parent_element: Weak<RefCell<Element>>,
371
372    /// List of elements that are not attached to the root anymore because they have been
373    /// optimized away, but their properties may still be in use
374    pub optimized_elements: RefCell<Vec<ElementRc>>,
375
376    /// The layout constraints of the root item
377    pub root_constraints: RefCell<LayoutConstraints>,
378
379    /// When creating this component and inserting "children", append them to the children of
380    /// the element pointer to by this field.
381    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    /// This component actually inherits PopupWindow (although that has been changed to a Window by the lower_popups pass)
389    pub inherits_popup_window: Cell<bool>,
390
391    /// The names under which this component should be accessible
392    /// if it is a global singleton and exported.
393    pub exported_global_names: RefCell<Vec<ExportedName>>,
394
395    /// True if this component is used as a sub-component by at least one other component.
396    pub used: Cell<bool>,
397
398    /// The list of properties (name and type) declared as private in the component.
399    /// This is used to issue better error in the generated code if the property is used.
400    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    /// This component is a global component introduced with the "global" keyword
444    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    /// Returns the names of aliases to global singletons, exactly as
453    /// specified in the .slint markup (not normalized).
454    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    // Number of repeaters in this component, including sub-components
464    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    /// for builtin properties that must be known at compile time and cannot be changed at runtime
486    Constexpr,
487    /// For builtin properties that are meant to just be bindings but cannot be read or written
488    /// (eg, Path's `commands`)
489    Fake,
490    /// For functions, not properties
491    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    /// Tells if getter and setter will be added to expose in the native language API
515    pub expose_in_public_api: bool,
516    /// Public API property exposed as an alias: it shouldn't be generated but instead forward to the alias.
517    pub is_alias: Option<NamedReference>,
518    pub visibility: PropertyVisibility,
519    /// For function or callback: whether it is declared as `pure` (None for private function for which this has to be deduced)
520    pub pure: Option<bool>,
521}
522
523impl PropertyDeclaration {
524    // For diagnostics: return a node pointing to the type
525    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    /// The state id as computed in lower_state
544    pub state_id: i32,
545    /// false for 'to', true for 'out'
546    pub is_out: bool,
547    /// The content of the `animation` object
548    pub animation: ElementRc,
549}
550
551impl TransitionPropertyAnimation {
552    /// Return an expression which returns a boolean which is true if the transition is active.
553    /// The state argument is an expression referencing the state property of type StateInfo
554    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/// Map the accessibility property (eg "accessible-role", "accessible-label") to its named reference
610#[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    // The id qualified with the enclosing component name. Given `foo := Bar {}` this is `EnclosingComponent::foo`
637    pub qualified_id: Option<SmolStr>,
638    pub type_name: String,
639    pub node: syntax_nodes::Element,
640    // Field to indicate whether this element was a layout that had
641    // been lowered into a rectangle in the lower_layouts pass.
642    pub layout: Option<crate::layout::Layout>,
643    /// Set to true if the ElementDebugInfo following this one in the debug vector
644    /// in Element::debug is the last one and the next entry belongs to an other element.
645    /// This can happen as a result of rectangle optimization, for example.
646    pub element_boundary: bool,
647}
648
649impl ElementDebugInfo {
650    // Returns a comma separate string that encodes the element type name (`Rectangle`, `MyButton`, etc.)
651    // and the qualified id (`SurroundingComponent::my-id`).
652    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/// An Element is an instantiation of a Component
663#[derive(Default)]
664pub struct Element {
665    /// The id as named in the original .slint file.
666    ///
667    /// Note that it can only be used for lookup before inlining.
668    /// After inlining there can be duplicated id in the component.
669    /// The id are then re-assigned unique id in the assign_id pass
670    pub id: SmolStr,
671    //pub base: QualifiedTypeName,
672    pub base_type: ElementType,
673    /// Currently contains also the callbacks. FIXME: should that be changed?
674    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    /// The component which contains this element.
680    pub enclosing_component: Weak<Component>,
681
682    pub property_declarations: BTreeMap<SmolStr, PropertyDeclaration>,
683
684    /// Main owner for a reference to a property.
685    pub named_references: crate::namedreference::NamedReferenceContainer,
686
687    /// This element is part of a `for <xxx> in <model>`:
688    pub repeated: Option<RepeatedElementInfo>,
689    /// This element is a placeholder to embed an Component at
690    pub is_component_placeholder: bool,
691
692    pub states: Vec<State>,
693    pub transitions: Vec<Transition>,
694
695    /// true when this item's geometry is handled by a layout
696    pub child_of_layout: bool,
697    /// The property pointing to the layout info. `(horizontal, vertical)`
698    pub layout_info_prop: Option<(NamedReference, NamedReference)>,
699    /// Whether we have `preferred-{width,height}: 100%`
700    pub default_fill_parent: (bool, bool),
701
702    pub accessibility_props: AccessibilityProps,
703
704    /// Reference to the property.
705    /// This is always initialized from the element constructor, but is Option because it references itself
706    pub geometry_props: Option<GeometryProps>,
707
708    /// true if this Element is the fake Flickable viewport
709    pub is_flickable_viewport: bool,
710
711    /// true if this Element may have a popup as child meaning it cannot be optimized
712    /// because the popup references it.
713    pub has_popup_child: bool,
714
715    /// This is the component-local index of this item in the item tree array.
716    /// It is generated after the last pass and before the generators run.
717    pub item_index: OnceCell<u32>,
718    /// the index of the first children in the tree, set with item_index
719    pub item_index_of_first_children: OnceCell<u32>,
720
721    /// True when this element is in a component was declared with the `:=` symbol instead of the `component` keyword
722    pub is_legacy_syntax: bool,
723
724    /// How many times the element was inlined
725    pub inline_depth: i32,
726
727    /// Debug information about this element.
728    ///
729    /// There can be several in case of inlining or optimization (child merged into their parent).
730    ///
731    /// The order in the list is first the parent, and then the removed children.
732    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        //writeln!(f, "; /*{}*/", expr.priority)?;
798        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    /*if let Type::Component(base) = &e.base_type {
833        pretty_print(f, &c.borrow(), indentation)?
834    }*/
835    indentation -= 1;
836    indent!();
837    writeln!(f, "}}")
838}
839
840#[derive(Clone, Default, Debug)]
841pub struct PropertyAnalysis {
842    /// true if somewhere in the code, there is an expression that changes this property with an assignment
843    pub is_set: bool,
844
845    /// True if this property might be set from a different component.
846    pub is_set_externally: bool,
847
848    /// true if somewhere in the code, an expression is reading this property
849    /// Note: currently this is only set in the binding analysis pass
850    pub is_read: bool,
851
852    /// true if this property is read from another component
853    pub is_read_externally: bool,
854
855    /// True if the property is linked to another property that is read only. That property becomes read-only
856    pub is_linked_to_read_only: bool,
857
858    /// True if this property is linked to another property
859    pub is_linked: bool,
860}
861
862impl PropertyAnalysis {
863    /// Merge analysis from base element for inlining
864    ///
865    /// Contrary to `merge`, we don't keep the external uses because
866    /// they should come from us
867    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    /// Merge the analysis
873    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    /// Return true if it is read or set or used in any way
881    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    /// The ListView's inner visible height (not counting eventual scrollbar)
892    pub listview_height: NamedReference,
893    /// The ListView's inner visible width (not counting eventual scrollbar)
894    pub listview_width: NamedReference,
895}
896
897#[derive(Debug, Clone)]
898/// If the parent element is a repeated element, this has information about the models
899pub struct RepeatedElementInfo {
900    pub model: Expression,
901    pub model_data_id: SmolStr,
902    pub index_id: SmolStr,
903    /// A conditional element is just a for whose model is a boolean expression
904    ///
905    /// When this is true, the model is of type boolean instead of Model
906    pub is_conditional_element: bool,
907    /// When the for is the delegate of a ListView
908    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            // This must be a global component it can only have properties and callback
950            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            // This should normally never happen because the parser does not allow for this
976            assert!(diag.has_errors());
977            return ElementRc::default();
978        } else {
979            tr.empty_type()
980        };
981        // This isn't truly qualified yet, the enclosing component is added at the end of Component::from_node
982        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                // Type::Void is used for two way bindings without type specified
1010                .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 => {} // Ok to proceed with a new declaration
1035                _ => {
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                // argument matching will happen later
1318            } 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    /// Return the type of a property in this element or its base, along with the final name, in case
1681    /// the provided name points towards a property alias. Type::Invalid is returned if the property does
1682    /// not exist.
1683    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    /// Returns the element's name as specified in the markup, not normalized.
1809    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    /// Return true if the binding is set, either on this element or in a base
1818    ///
1819    /// If `need_explicit` is true, then only consider binding set in the code, not the ones set
1820    /// by the compiler later.
1821    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    /// Set the property `property_name` of this Element only if it was not set.
1834    /// the `expression_fn` will only be called if it isn't set
1835    ///
1836    /// returns true if the binding was changed
1837    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
1897/// Apply default property values defined in `builtins.slint` to the element.
1898fn apply_default_type_properties(element: &mut Element) {
1899    // Apply default property values on top:
1900    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
1913/// Create a Type for this node
1914pub 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
1943/// Create a [`Type::Struct`] from a [`syntax_nodes::ObjectType`]
1944pub 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
2020/// Return a NamedReference for a qualified name used in a state (or transition),
2021/// if the reference is invalid, there will be a diagnostic
2022fn 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
2080/// FIXME: this is duplicated the resolving pass. Also, we should use a hash table
2081fn 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
2097/// Find the parent element to a given element.
2098/// (since there is no parent mapping we need to fo an exhaustive search)
2099pub 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
2119/// Call the visitor for each children of the element recursively, starting with the element itself
2120///
2121/// The state returned by the visitor is passed to the children
2122pub 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
2133/// Same as [`recurse_elem`] but include the elements from sub_components
2134pub 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
2160/// Same as recurse_elem, but will take the children from the element as to not keep the element borrow
2161pub 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
2173/// Same as [`recurse_elem`] but include the elements form sub_components
2174pub 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
2201/// This visit the binding attached to this element, but does not recurse in children elements
2202/// Also does not recurse within the expressions.
2203///
2204/// This code will temporarily move the bindings or states member so it can call the visitor without
2205/// maintaining a borrow on the RefCell.
2206pub 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        // This is not really a named reference, but the result is the same, it need to be updated
2291        // FIXME: this should probably be lowered into a PropertyReference
2292        Expression::RepeaterModelReference { element }
2293        | Expression::RepeaterIndexReference { element } => {
2294            // FIXME: this is questionable
2295            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
2305/// Visit all the named reference in an element
2306/// But does not recurse in sub-elements. (unlike [`visit_all_named_references`] which recurse)
2307pub 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    // visit two way bindings
2364    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
2377/// Visit all named reference in this component and sub component
2378pub 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
2406/// Visit all expression in this component and sub components
2407///
2408/// Does not recurse in the expression itself
2409pub 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    /// false for 'to', true for 'out'
2428    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, // normalized
2471    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                // re-export are handled in the TypeLoader::load_dependencies_recursively_impl
2548                .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            // Skip over duplicates and issue warnings
2610            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
2722/// This function replace the root element of a repeated element. the previous root becomes the only
2723/// child of the new root element.
2724/// Note that no reference to the base component must exist outside of repeated_element.base_type
2725pub fn inject_element_as_repeated_element(repeated_element: &ElementRc, new_root: ElementRc) {
2726    let component = repeated_element.borrow().base_type.as_component().clone();
2727    // Since we're going to replace the repeated element's component, we need to assert that
2728    // outside this function no strong reference exists to it. Then we can unwrap and
2729    // replace the root element.
2730    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    // Any elements with a weak reference to the repeater's component will need fixing later.
2736    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        // generate the layout_info_prop that forward to the implicit layout for that item
2752        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    // Replace the repeated component's element with our shadow element. That requires a bit of reference counting
2775    // surgery and relies on nobody having a strong reference left to the component, which we take out of the Rc.
2776    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
2793/// Make the geometry of the `injected_parent` that of the old_elem. And the old_elem
2794/// will cover the `injected_parent`
2795pub 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    // (should be removed by const propagation in the llr)
2805    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}