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