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