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