i_slint_compiler/
object_tree.rs

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