Skip to main content

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